This has nothing to do with loops.
This behavior is triggered because you use a lambda expression () => variable * 2
where the outer scoped variable
not actually defined in the lambda's inner scope.
Lambda expressions (in C#3+, as well as anonymous methods in C#2) still create actual methods. Passing variables to these methods involve some dilemmas (pass by value? pass by reference? C# goes with by reference - but this opens another problem where the reference can outlive the actual variable). What C# does to resolve all these dilemmas is to create a new helper class ("closure") with fields corresponding to the local variables used in the lambda expressions, and methods corresponding to the actual lambda methods. Any changes to variable
in your code is actually translated to change in that ClosureClass.variable
So your while loop keeps updating the ClosureClass.variable
until it reaches 10, then you for loops executes the actions, which all operate on the same ClosureClass.variable
.
To get your expected result, you need to create a separation between the loop variable, and the variable that is being closured. You can do this by introducing another variable, i.e.:
List<Func<int>> actions = new List<Func<int>>();
int variable = 0;
while (variable < 5)
{
var t = variable; // now t will be closured (i.e. replaced by a field in the new class)
actions.Add(() => t * 2);
++variable; // changing variable won't affect the closured variable t
}
foreach (var act in actions)
{
Console.WriteLine(act.Invoke());
}
You could also move the closure to another method to create this separation:
List<Func<int>> actions = new List<Func<int>>();
int variable = 0;
while (variable < 5)
{
actions.Add(Mult(variable));
++variable;
}
foreach (var act in actions)
{
Console.WriteLine(act.Invoke());
}
You can implement Mult as a lambda expression (implicit closure)
static Func<int> Mult(int i)
{
return () => i * 2;
}
or with an actual helper class:
public class Helper
{
public int _i;
public Helper(int i)
{
_i = i;
}
public int Method()
{
return _i * 2;
}
}
static Func<int> Mult(int i)
{
Helper help = new Helper(i);
return help.Method;
}
In any case, "Closures" are NOT a concept related to loops, but rather to anonymous methods / lambda expressions use of local scoped variables - although some incautious use of loops demonstrate closures traps.
Captured variables are always evaluated when the delegate is actually invoked, not when the variables were captured
. – Radiatorforeach
loops in C# 5.0 butfor
loops continue to behave the same as detailed here. – Radiator