I've noticed the following. This C# code:
List<Action> methodList = new List<Action>();
for (int iFn = 0; iFn < 4; ++iFn)
{
void thisLocalFunction()
{
string output = iFn.ToString();
Console.WriteLine(output);
}
methodList.Add(thisLocalFunction);
}
for (int iFn = 0; iFn < methodList.Count; ++iFn)
{
methodList[iFn]();
}
Produces 4, 4, 4, 4. On the other hand, this code:
List<Action> methodList = new List<Action>();
for (int iFn = 0; iFn < 4; ++iFn)
{
string output = iFn.ToString();
void thisLocalFunction()
{
Console.WriteLine(output);
}
methodList.Add(thisLocalFunction);
}
for (int iFn = 0; iFn < methodList.Count; ++iFn)
{
methodList[iFn]();
}
Produces 0, 1, 2, 3.
I'm not sure I understand why. I've read a bit about "capturing" but I'm not sure if that's related to what's going on here. Could someone give me a breakdown of why the two implementations behave differently?
The critical bit of code that you've moved,
string output = iFn.ToString();
turnsiFn
into a string when it is runIn the first example, it is run after the loop has finished (but "by magic" a single
iFn
is still available). Of course, after the loop finishes, iFn is 4 (because that's how the loop stopped). Why is it run after the loop finishes? because you "created a method and stored it in a variable" and the call to turniFn
into a string is inside this method. You didn't run the method you created while you were in the loop, so iFn was never turned into a string while you were in the loop. The method-in-a-variable was only run afterwards, and because the method contains the code that turnsiFn
into a string, it accessesiFn
at whatever current value it has, which is 4.In the second example iFn is turned into a string as the loop is executing so the value is 0, then 1, then 2, then 3. This is stored in a new variable (called
output
) that the method you created in the loop, has access to (again, let's say the method can still accessoutput
, even though it looks like it's out of scope, "by magic"), but the value ofoutput
is generated on each pass of the loop and then given to the method. In essence each of your 4 methods-as-a-variable stored in the list has access to a different variable calledoutput
- the first method' output has a value of 0, the second method's variable calledoutput
has a value of 1..You could conceive that when you create a method that you will later invoke, the "environment variables" the method had access to at the time it was created, are packaged up with it so that it has its own little environment to execute in. In the first case, each of the 4 methods has access to
iFn
at whatever value it is now, whereas in the second they have access tooutput
at whatever value it had when it was in the loop. Just because the variable is named the same on each pass of the loop doesn't mean it's reusing the same memory location to hold the data