Captured Closure (Loop Variable) in C# 5.0
This works fine (means as expected) in C# 5.0:
var actions = new List<Action>();
foreach (var i in Enumerable.Range(0, 10))
{
actions.Add(() => Console.WriteLine(i));
}
foreach (var act in actions) act();
Prints 0 to 9. But this one shows 10 for 10 times:
var actions = new List<Action>();
for (var i = 0; i < 10; i++)
{
actions.Add(() => Console.WriteLine(i));
}
foreach (var act in actions) act();
Question: This was a problem that we had in C# versions before 5.0; so we had to use a loop-local placeholder for the closure and it's fixed now - in C# 5.0 - in "foreach" loops. But not in "for" loops!
What is the reasoning behind this (not fixing the problem for for
loops too)?
What is the reasoning behind this?
I'm going to assume you mean "why wasn't it changed for for
loops as well?"
The answer is that for for
loops, the existing behaviour makes perfect sense. If you break a for
loop into:
- initializer
- condition
- iterator
- body
... then the loop is roughly:
{
initializer;
while (condition)
{
body;
iterator;
}
}
(Except that the iterator
is executed at the end of a continue;
statement as well, of course.)
The initialization part logically only happens once, so it's entirely logical that there's only one "variable instantiation". Furthermore, there's no natural "initial" value of the variable on each iteration of the loop - there's nothing to say that a for
loop has to be of a form declaring a variable in the initializer, testing it in the condition and modifying it in the iterator. What would you expect a loop like this to do:
for (int i = 0, j = 10; i < j; i++)
{
if (someCondition)
{
j++;
}
actions.Add(() => Console.WriteLine(i, j));
}
Compare that with a foreach
loop which looks like you're declaring a separate variable for every iteration. Heck, the variable is read-only, making it even more odd to think of it being one variable which changes between iterations. It makes perfect sense to think of a foreach
loop as declaring a new read-only variable on each iteration with its value taken from the iterator.