我如何/应该用一个linq查询替换嵌套的foreach

本文关键字:linq 一个 查询 替换 foreach 嵌套 | 更新日期: 2023-09-27 18:18:21

尝试在Windows窗体组框中包含的某些标签上设置属性,我编写了下面的循环。这工作得很好,但我不喜欢它,因为它的(我认为不必要的)双foreach嵌套。

我试着重写它,使其更清晰,只使用一个foreach和一个组合的Linq表达式,但是我所有的尝试都在运行时失败,从GroupBox到Label或反之亦然。

是否有更清晰、更有效或更可读的方式来编写这个循环结构?

        foreach (var gb in (from Control c in this.Controls where c is GroupBox select c))
            foreach (Label tlbl in (from Control a in gb.Controls
                                    where a is Label && a.Tag != null && a.Tag.ToString() == "answerswer"
                                    select a))
                tlbl.ForeColor = (tlbl.Name.Replace("lbl", "") == rb.Name) ? afterSelectColor : beforeSelectColor;
可读性是我的最高目标。考虑到这一点,是否值得尝试重写它?

我如何/应该用一个linq查询替换嵌套的foreach

我建议您在foreach中进行编辑,因为LINQ并不意味着会产生副作用。这样的:

foreach (Label tlbl in (this.Controls.OfType<GroupBox>()
    .SelectMany(g => g.Controls.Cast<Control>()).OfType<Label>()
    .Where(a => a.Tag != null && a.Tag.ToString() == "answerswer")))
{
    tblb.ForeColour = tlbl.Name.Replace("lbl", "") == rb.Name ? afterSelectColor : beforeSelectColor;
}

注意这里的SelectMany。这就是如何将嵌套的foreach循环转换为LINQ,因为它几乎只是一个嵌套的foreach循环。

Controls.OfType<GroupBox>
        .SelectMany(x => x.Controls.OfType<Label>)
        .Where(x => x.Tag != null && x.Tag.ToString() == "answerswer")
        .ToList()
        .ForEach(x => x ForeColor = (x.Name.Replace("lbl", "") == rb.Name) ? afterSelectColor : beforeSelectColor);

注意ForEach()方法不是LINQ的一部分。它是List<T>类的成员。LinQ是一个函数特性,因此它的方法不应该影响源对象。这就是为什么LINQ中没有ForEach()

编辑:

如果您不喜欢使用List<T>.ForEach(),那么您也可以这样做:

var labels = Controls.OfType<GroupBox>
                     .SelectMany(x => x.Controls.OfType<Label>)
                     .Where(x => x.Tag != null && x.Tag.ToString() == "answerswer")
foreach (var label in labels)
{
  label.ForeColor = (label.Name.Replace("lbl", "") == rb.Name) ? afterSelectColor : beforeSelectColor);
}

虽然这将代码分成两个语句,但与其他方法相比,它大大提高了可读性。

Edit2:

由于这是winforms, Control.Controls集合不是IEnumerable<T>,而是IEnumerable,因此OfType<T>必须包含在SelectMany()表达式中。纠正。