为什么ReSharper建议我将for循环转换为LINQ表达式?

27
在 Visual Studio 中,Re-Sharper 一直建议我将 for 循环转换为 linq 表达式,但是这样做的原因是什么?
哪个更快?
以下是一些示例循环,其中 Resharper 建议进行 Linq 转换:
foreach (XmlNode legendEntryNode in _legendEntryNodes)
{
    var xmlElement = legendEntryNode["FeatureType"];

    if (xmlElement == null || !xmlElement.InnerText.Equals(featuretype)) continue;

    var xmlNodeList = legendEntryNode.SelectNodes("Themes/Theme");

    if (xmlNodeList != null)

    foreach (XmlNode themeNode in xmlNodeList)
    {
        var element = themeNode["Value"];

        if (element == null || !element.InnerText.Equals(v)) continue;

        var xmlElement1 = themeNode["Icon"];

        if (xmlElement1 != null)
        {
            string iconname = "<ms:ICON>" + xmlElement1.InnerText + "</ms:ICON>";

            var element1 = themeNode["Highlight"];

            if (element1 != null)
            {
                string highlightname = "<ms:HIGHLIGHT>" + element1.InnerText + "</ms:HIGHLIGHT>";
                gml = gml.Insert(c, iconname + highlightname);

                c += (iconname.Length + highlightname.Length);
            }
        }
        break;
    }
}

还有这个更简单的例子:

for (int i = 0; i < getPointsRequest.Attribs.Length; i++)
{
    string attribName = getPointsRequest.Attribs[i].AttributeName;

    if (!String.IsNullOrEmpty(attribName))
    {
        sqlQuery += "<ms:" + attribName + ">||\"" + attribName + "\"||</ms:" + attribName + ">";
    }
}

你可能想给我们展示一个循环的例子。 - Tim Schmelter
1
只是一个小小的事情,我知道这些只是示例,但您可能想将字符串连接更改为 string.Format,以便简单示例变为:sqlQuery += string.Format("<ms:{0}>||\"{0}\"||</ms:{0}>", attribName);,这样更容易阅读。Resharper 使更改它们变得很容易,因为它是其重构选项之一。 - Piers Myers
(顺便说一句,在问题标题中,反引号无法用于代码格式化) - AakashM
你可能还想使用 StringBuilder 而不是字符串拼接的方式来构建 sqlQuery。 - Ami Schreiber
7个回答

42

速度通常在你的代码大部分中并不重要 - 你应该以最简单的方式编写代码,然后进行测量以确保速度足够

如果您的for循环只是查询,则使用LINQ绝对是编写更易读的代码的好方法。它并非普遍适用,但至少应该经常牢记

很多时候,for循环可以转换为查询以进行惰性评估,然后是对由查询返回的每个值执行某些操作的foreach循环。这可以帮助将两个方面分开,使您在阅读代码时专注于一个方面。但是,重要的是要将LINQ查询保持为查询,而不是在其中使用副作用 - 它被设计为具有函数式方法,与副作用真的没有很好的结合。

如果您有一些具体的示例,我们可以就哪些循环转换为使用LINQ会有意义,哪些不会给出更多意见。


8
这是一个很大的假设,你应该在试图微调代码之前进行验证。通常性能受架构和整体设计的影响比单个代码部分更大,瓶颈也经常出现在IO而不是处理上。你应该确定你的性能要求,测试满足需求的最清晰的代码的性能,然后如果你需要优化,使用分析器有针对性地进行优化。我肯定不会假设所有代码区域对整体性能的贡献都是均匀的。 - Jon Skeet
好的,谢谢你。非常感激。相比许多其他开发者,我是一个相对较新的开发者,有时候会忽略一些事情,因为通常我只负责项目的一小部分,所以我希望确保我的部分能够顺利、快速地运行,并且完全满足要求。 - CSharpened
7
如果你是作为团队的一部分工作,你应该询问你的团队成员关于项目中你所负责的部分的性能要求。他们可能没有考虑过这个问题-你应该巧妙地建议整个系统进行基准测试,并且如果整体性能不令人满意,对哪些区域进行剖析以确定瓶颈。 - Jon Skeet
@rolls:你可能不是唯一一个这样的人,但我怀疑你很少有人像你这样,并且可能只是没有给LINQ足够的机会。需要一点时间来进入思维模式,但它随后变得非常有用。我鼓励你至少再试一下。 - Jon Skeet
1
哦,我肯定会使用它,但是我仍然经常使用for/foreach循环,特别是对于低级别的东西。我相信大多数C程序员和我一样,他们中的大多数可能从未接触过Java/C#,因此他们可能甚至不知道它们是否可读。 - rollsch
显示剩余4条评论

18

1
一个程序员更倾向于编写更接近自然语言的代码... 显然,LINQ 有助于实现这一点... - Umer

6

通常,ReSharper的建议只是建议而不是警告。因此,选择使用LINQ还是foreach取决于您自己。
我也遇到了“使用'var'”这个建议的问题。只有在我认为读者可以更好地阅读语句时,我才会点击该建议。
可读性是我编写代码时最重要的优先事项之一。


4
我认为有时不转换的原因是有道理的。ReSharper没有提供将LINQ表达式(重新)转换为for循环的重构,这可能不太令人钦佩。我曾经将循环转换为表达式,然后稍后想在循环内放入一些其他操作(通常是调试操作); 我必须手工将它们转换回来。
我会警告不要没有充分理由地转换for循环。相当多的情况下,它并不能真正提高可读性,也没有任何其他强烈的理由去做它(正如其他人所指出的,大多数循环并不关键速度)。
我认为有些for循环比LINQ等效更可读,因为它们可以将循环的操作视觉上分解成小块。我会说,将三到四行的小循环变成一行表达式最容易改善。
[很抱歉,本帖子大部分是主观观点,但可读性是一个相对主观的话题。请勿恶意攻击!]

4

很可能速度没有区别,然而使用Linq通常会导致代码更加简洁。 这并不意味着您应该始终接受R#的建议将其转换为Linq表达式。有时候复杂但易于理解的foreach循环被转换为有效但不易理解的Linq表达式。


从技术上讲,for循环总是更快的,因为linq表达式需要方法调用。 - Polity
这是我对它的主要问题之一。我不太熟悉linq,因此我发现一些转换后的代码很难阅读,这实际上与resharper最可能的意图相反。我怀疑其他不使用linq的开发人员也会遇到同样的问题,这可能会使我的代码中的某些部分难以阅读,对于那些还没有掌握linq的人来说,他们也会感到困惑。 - CSharpened
1
Linq 的整个意义在于编写更少冗长、更易读的代码——它可能在某些情况下显得冗长,但这要么是例外情况,要么是读者未能欣赏函数式/声明式代码的失败。你基本上永远不想像 foreach 循环一样编写 Linq 代码,例如:from s in selection.ShapeRange.Cast<Shape>() where s.IsSomething() select s.Name - PandaWood

2

0
作为其他人的参考,这里有一个for循环的例子和Resharper建议的for循环。
for (int x = 0; x < grid.Length; x++)
        {
            var intCount = grid[x].Select((a, b) => new {Value = a, Index = b})
                .GroupBy(y => y.Value)
                .Where(y => y.Count() > 1).Select(item => item.Key).ToArray();

            if (intCount.Count() > 1)
                return false;
        }

为了解释这段代码,这个for循环将获取数组中的所有重复项。在获取所有重复项之后,检查项目数量是否大于1,如果是,则返回false。
这是LINQ中建议使用的for循环:
  return grid.Select(t => t.Select((a, b) => new {Value = a, Index = b}).
            GroupBy(y => y.Value).Where(y => y.Count() > 1).
            Select(item => item.Key).ToArray()).All(intCount => intCount.Count() <= 1);

可能没有性能提升,但正如您从示例中所看到的那样,LINQ查询更加简洁、易于阅读、代码行数更少(在这种情况下,只有一行代码,我在此处粘贴后进行了调整),而且也更容易调试。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接