Closures behave differently in for and foreach loops

During experiments with closing in C #, I found that they work quite unexpectedly if they capture an iterator variable in a loop.

var actions = new List<Action>(); foreach (int i in new[] { 1, 2 }) actions.Add(() => Console.WriteLine(i)); for (int i = 3; i <= 4; i++) actions.Add(() => Console.WriteLine(i)); foreach (var action in actions) action(); 

The above code produces a weird result (I am using the .NET 4.5 compiler):

 1 2 5 5 

Why is the value of i written differently for 2 almost identical cycles?

+4
source share
2 answers

In C # 5 onwards, the foreach declares a separate variable i for each iteration of the loop. Thus, each closure captures a separate variable, and you see the expected results.

In the for loop, you have only one variable i , which is fixed by all closures and changes as the cycle progresses - therefore, by the time delegates are called, you will see the final value of this only variable.

In C # 2, 3, and 4, the foreach also behaved in a way that in principle was never the desired behavior, so it was fixed in C # 5.

You can achieve the same effect in a for loop if you introduce a new variable in the body area of ​​the loop:

 for (int i = 3; i <= 4; i++) { int copy = i; actions.Add(() => Console.WriteLine(copy)); } 

For more information, read Eric Lippert's blog post, “Closing on a loop variable is considered harmful” - Part 1 , Part 2 .

+7
source

In the case of foreach, it contains a value in a local variable, so it has its own value for each delegate, and in the case of a loop this is not so, in the case of a loop, all delegates refer to the same i as the last value updated in i , is used all delegates.

It was a terrific change in the case of the foreach loop, both worked the same in older versions.

0
source

Source: https://habr.com/ru/post/1243162/


All Articles