Lambda variable capture in loop - what happens here? [duplicate]
In this line
listActions.Add(() => Console.WriteLine(i));
the variable i
, is captured, or if you wish, created a pointer to the memory location of that variable. That means that every delegate got a pointer to that memory location. After this loop execution:
foreach (int i in Enumerable.Range(1, 10))
{
listActions.Add(() => Console.WriteLine(i));
}
for obvious reasons i
is 10
, so the memory content that all pointers present in Action
(s) are pointing, becomes 10.
In other words, i
is captured.
By the way, should note, that according to Eric Lippert, this "strange" behaviour would be resolved in C# 5.0
.
So in the C# 5.0
your program would print as expected:
1,2,3,4,5...10
EDIT:
Can not find Eric Lippert's post on subject, but here is another one:
Closure in a Loop Revisited
What sort of code does the compiler produce ?
It sounds like you expect this:
foreach (int temp in Enumerable.Range(1, 10))
{
int i = temp;
listActions.Add(() => Console.WriteLine(i));
}
That uses a different varaible for each iteration, and so the value of the variable at the time the lambda is created will be captured. In fact, you could use this exact code and get the result you want.
But what the compiler actually does is something closer to this:
int i;
foreach (i in Enumerable.Range(1, 10))
{
listActions.Add(() => Console.WriteLine(i));
}
This makes it clear that you are capturing the same variable on each iteration. When you later go and actually execute that code, they all refer to the same value that has already be incremented up to 10.
This is not a bug in the compiler or runtime... it was a conscious decision on the part of the language design team. However, due to confusion about how this works, they have reversed that decision and decided to risk a breaking change to make it work more like you expect for C# 5.