Linq Where local counter closure different results in VS watch

烂漫一生 提交于 2019-12-23 12:11:12

问题


I try to delete first 3 elements in array with LinQ Where extension function.

Here is an example:

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)}}}");

It works fine and in each case i have { 4, 5, 6, 7, 8, 9 } result in console.

But in watch in Visual Studio 2015 Update 3 in test2 and test3 case I have wrong result:

Can anyone explain why everything is ok when I use .ToList() and .ToArray() and wrong in other cases?

Is that bug?


回答1:


The difference is due to deferred execution of the lambda with side effects. You have to be very careful here, because the lambda is evaluated each time that IEnumerable<T> produced by Where gets enumerated, causing its side effects (i.e. incrementing deletedTestX) again and again.

When you run the program, each of your four sequences is enumerated exactly once. For cases 1 and 4 the enumeration happens inside ToList and ToArray, while for cases 2 and 3 it happens inside string.Join.

When you open the results in the debugger, the controller of the watch window must run your enumeration in order to show you the results. This is the second enumeration of your sequence, so it happens with side effects of the first enumeration already applied. That's why you see wrong indexes in the debug window.

You can reproduce this behavior in your program by printing each result twice:

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.



来源:https://stackoverflow.com/questions/45653370/linq-where-local-counter-closure-different-results-in-vs-watch

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!