While experimenting with closures in C# I found out that they work rather 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 strange result (I'm using .NET 4.5 compiler):
1
2
5
5
Why is the value of i
captured differently for 2 almost identical loops?
In C# 5 and beyond, the
foreach
loop declares a separatei
variable for each iteration of the loop. So each closure captures a separate variable, and you see the expected results.In the
for
loop, you only have a singlei
variable, which is captured by all the closures, and modified as the loop progresses - so by the time you call the delegates, you see the final value of that single variable.In C# 2, 3 and 4, the
foreach
loop behaved that way as well, which was basically never the desired behaviour, so it was fixed in C# 5.You can achieve the same effect in the
for
loop if you introduce a new variable within the scope of the loop body:For more details, read Eric Lippert's blog posts, "Closing over the loop variable considered harmful" - part 1, part 2.
In foreach case it holds the value in a local variable so it has its own value for every delegate while in for loop case it is not like that, in for loop case all the the delegates are referring to same i so the last value updated in the
i
is used by all delegates.That was a breaking change in case of foreach loop, in old versions they both used to be work same way.