这与循环无关。
触发此行为的原因是,您使用的Lambda表达式() => variable * 2
的外部作用域variable
实际上未在Lambda的内部作用域中定义。
Lambda表达式(在C#3 +中以及在C#2中为匿名方法)仍会创建实际方法。将变量传递给这些方法会遇到一些难题(按值传递吗?按引用传递?C#随引用一起使用-但这带来了另一个问题,即引用可能会超出实际变量的寿命)。C#解决所有这些难题的方法是创建一个新的帮助器类(“ closure”),该类具有与lambda表达式中使用的局部变量相对应的字段以及与实际lambda方法相对应的方法。variable
您对代码的任何更改实际上都会翻译为更改ClosureClass.variable
因此,您的while循环会不断更新ClosureClass.variable
直到达到10,然后for循环会执行操作,所有操作都在同一上进行ClosureClass.variable
。
为了获得预期的结果,您需要在循环变量和要关闭的变量之间创建一个分隔。您可以通过引入另一个变量来做到这一点,即:
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());
}
您也可以将闭包移动到另一个方法来创建此分离:
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());
}
您可以将Mult实现为lambda表达式(隐式关闭)
static Func<int> Mult(int i)
{
return () => i * 2;
}
或使用实际的辅助课程:
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;
}
在任何情况下,“闭包”都不是与循环相关的概念,而是与局部范围变量使用的匿名方法/ lambda表达式相关的信息-尽管对循环的某些谨慎使用显示了闭包陷阱。