问题
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