Foreach和Linq声明均等化

时间:2014-09-24 13:52:58

标签: c# linq resharper

据Resharper说,这两个人也应该这样做:

1)

string strTemp = null;
foreach (var s in array)
{
    if (strTemp == null || !strTemp.Contains(s))
    {
        strTemp += (!string.IsNullOrEmpty(strTemp) ? ";" : string.Empty) + s;
    }
}

2)

string strTemp = null;
foreach (var s in array.Where(s => strTemp == null || !strTemp.Contains(s)))
{
    strTemp += (!string.IsNullOrEmpty(strTemp) ? ";" : string.Empty) + s;
}

怎么样?我错过了什么?

3 个答案:

答案 0 :(得分:5)

根据评论判断,您预计此lambda表达式只会执行一次,而strTemp的值仍为null

s => strTemp == null || !strTemp.Contains(s)

事实并非如此。它将为数组的每个元素执行,并在每种情况下使用最新值strTemp。即使只调用Where方法一次,也会为array中的每个元素执行lambda表达式(或者更确切地说,是从它创建的委托)。

您的代码可能更好地写为:

string joined = string.Join(";", array.Distinct());

......但是。特别是,在您当前的代码中,如果您的数组是{ "ab", "a", "ab", "c" },那么您最终会得到ab;c的输出,其中我怀疑您希望{{1} } ...这是因为ab;a;c已经包含在" ab"通过循环的第二次迭代。当然,如果你真的想要这种行为,那么你所获得的代码将会起作用......但对我来说这听起来并不常见。

编辑:要理解这一切,您需要了解LINQ如何使用延迟评估。 "a"方法立即返回,而没有执行谓词 at all ...但是当您向集合询问其第一个元素时(通过Where / GetEnumerator())它将迭代数组,针对每个项目测试谓词,直到找到匹配的谓词。然后它会产生该项,当你向它询问 next 项时,它将继续从它到达的位置,再次针对数组的其余元素测试谓词。

用一些代码证明这一点可能最容易:

MoveNext()

输出:

using System;
using System.Linq;

class Test
{
    static void Main()
    {
        var numbers = new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
        var filtered = numbers.Where(x => {
            Console.WriteLine("Testing {0}", x);
            return x % 2 == 0;
        });
        Console.WriteLine("Just before loop");
        foreach (var item in filtered)
        {
            Console.WriteLine("Received {0}", item);
        }
    }
}

如你所见:

  • 在我们开始迭代之前,没有从谓词打印任何内容
  • 输出将测试与每个包含项目的处理交错处理

答案 1 :(得分:0)

版本2将if - 条件从版本1重构为Where语句中foreach函数调用的过滤器。

答案 2 :(得分:0)

if-block和.Where方法填充相同的函数。

在选项1中,您遍历整个数组并检查变量是否与if语句匹配。

在选项2中,您对数组应用了一个过滤器,该过滤器等同于选项1中的if条件。

然后strTemp被处理。过滤后的数据在两种情况下都是相同的,因为if语句和where子句是相同的。