Linq Where local counter closure 在 VS watch 中产生不同的结果

Linq Where local counter closure different results in VS watch

我尝试使用 LinQ Where 扩展函数删除 array 中的前 3 个元素。

这是一个例子:

var array = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var count = 3;
var deletedTest1 = 0;
var test1 = array.Where(x => ++deletedTest1 > count).ToList();
Console.WriteLine($"{{{String.Join(", ", test1)}}}");
var deletedTest2 = 0;
var test2 = array.Where(x => ++deletedTest2 > count).AsEnumerable();
Console.WriteLine($"{{{String.Join(", ", test2)}}}");
var deletedTest3 = 0;
var test3 = array.Where(x => ++deletedTest3 > count);
Console.WriteLine($"{{{String.Join(", ", test3)}}}");
var deletedTest4 = 0;
var test4 = array.Where(x => ++deletedTest4 > count).ToArray();
Console.WriteLine($"{{{String.Join(", ", test4)}}}");

它工作正常,在每种情况下我都有 { 4, 5, 6, 7, 8, 9 } 结果 console.

但是在 在 Visual Studio 2015 更新 3 在 test2test3 的情况下我有错误的结果:

谁能解释为什么我使用 .ToList().ToArray() 时一切正常,而在其他情况下却出错?

这是错误吗?

不同之处在于延迟执行具有副作用的 lambda。在这里你必须非常小心,因为每次 Where 产生的 IEnumerable<T> 被枚举时都会计算 lambda,导致它的副作用(即递增 deletedTestX)一次又一次。

当您 运行 程序时,您的四个序列中的每一个都被恰好枚举一次。对于情况 1 和 4,枚举发生在 ToListToArray 内,而对于情况 2 和 3,它发生在 string.Join.

当您在调试器中打开结果时,手表的控制器 window 必须 运行 您的枚举才能显示结果。这是您的序列的第二个枚举,因此它发生在已经应用第一个枚举的副作用的情况下。这就是您在调试 window.

中看到错误索引的原因

您可以通过将每个结果打印两次来在您的程序中重现此行为:

var array = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var count = 3;
var deletedTest1 = 0;
var test1 = array.Where(x => ++deletedTest1 > count).ToList();
Console.WriteLine("Test 1, deletedTest1={0}", deletedTest1);
Console.WriteLine($"{{{String.Join(", ", test1)}}}");
Console.WriteLine("Test 1, deletedTest1={0}", deletedTest1);
Console.WriteLine($"{{{String.Join(", ", test1)}}}");
var deletedTest2 = 0;
var test2 = array.Where(x => ++deletedTest2 > count).AsEnumerable();
Console.WriteLine("Test 2, deletedTest2={0}", deletedTest2);
Console.WriteLine($"{{{String.Join(", ", test2)}}}");
Console.WriteLine("Test 2, deletedTest2={0}", deletedTest2);
Console.WriteLine($"{{{String.Join(", ", test2)}}}");
var deletedTest3 = 0;
var test3 = array.Where(x => ++deletedTest3 > count);
Console.WriteLine("Test 3, deletedTest3={0}", deletedTest3);
Console.WriteLine($"{{{String.Join(", ", test3)}}}");
Console.WriteLine("Test 3, deletedTest3={0}", deletedTest3);
Console.WriteLine($"{{{String.Join(", ", test3)}}}");
var deletedTest4 = 0;
var test4 = array.Where(x => ++deletedTest4 > count).ToArray();
Console.WriteLine("Test 4, deletedTest4={0}", deletedTest4);
Console.WriteLine($"{{{String.Join(", ", test4)}}}");
Console.WriteLine("Test 4, deletedTest4={0}", deletedTest4);
Console.WriteLine($"{{{String.Join(", ", test4)}}}");

Demo.