I am trying to get my head around, what happens here ? What sort of code does the compiler produce?
public static void vc()
{
var listActions = new List<Action>();
foreach (int i in Enumerable.Range(1, 10))
{
listActions.Add(() => Console.WriteLine(i));
}
foreach (Action action in listActions)
{
action();
}
}
static void Main(string[] args)
{
vc();
}
output: 10 10 .. 10
According to this, a new instance of ActionHelper would be created for every iteration. So in that case, I would assume it should print 1..10. Can someone give me some pseudo code of what the compiler is doing here ?
Thanks.
Much like functions can change the value of arguments passed by reference, we can also capture variables by reference to allow our lambda to affect the value of the argument. To capture a variable by reference, we prepend an ampersand ( & ) to the variable name in the capture.
A lambda expression can refer to identifiers declared outside the lambda expression. If the identifier is a local variable or a reference with automatic storage duration, it is an up-level reference and must be "captured" by the lambda expression.
The capture list defines the outside variables that are accessible from within the lambda function body. The only capture defaults are. & (implicitly capture the used automatic variables by reference) and. = (implicitly capture the used automatic variables by copy).
A captured variable is a variable declared outside of the scope of a lambda expression, but used within the expression.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With