Why does the following code print 11 twice?
int i = 10;
Action fn1 = () => Console.WriteLine(i);
i = 11;
Action fn2 = () => Console.WriteLine(i);
fn1();
fn2();
Output 11 11
According to the answers in this post - How to tell a lambda function to capture a copy instead of a reference in C#? - a lambda is translated into a class with a copy of the captured variable. If that is the case shouldn't my example have printed 10 & 11?
Now, when a lambda captures by reference how does it affect the life time of the variable captured. For instance, assume that the above piece of code was in a function & the Actions' scope was global to the variable like this:
class Test
{
Action _fn1;
Action _fn2;
void setActions()
{
int i = 10;
_fn1 = () => Console.WriteLine(i);
i = 11;
_fn2 = () => Console.WriteLine(i);
}
static void Main()
{
setActions();
_fn1();
_fn2();
}
}
In this scenario wouldn't the variable i have gone out of scope when the actions are invoked? So, are the actions left with a dangling pointer sort of reference?
If that is the case shouldn't my example have printed 10 & 11?
No, because you've only got a single variable - fn1
captures the variable, not its current value. So a method like this:
static void Foo()
{
int i = 10;
Action fn1 = () => Console.WriteLine(i);
i = 11;
Action fn2 = () => Console.WriteLine(i);
fn1();
fn2();
}
is translated like this:
class Test
{
class MainVariableHolder
{
public int i;
public void fn1() => Console.WriteLine(i);
public void fn2() => Console.WriteLine(i);
}
void Foo()
{
var holder = new MainVariableHolder();
holder.i = 10;
Action fn1 = holder.fn1;
holder.i = 11;
Action fn2 = holder.fn2;
fn1();
fn2();
}
}
This answers your second point too: the variable is "hoisted" into a class, so its lifetime is effectively extended as long as the delegate is alive.
See more on this question at Stackoverflow