这与循环无关。
触发此行为是因为您使用了 lambda 表达式() => variable * 2
,其中外部范围variable
实际上并未在 lambda 的内部范围中定义。
Lambda 表达式(在 C#3+ 中,以及在 C#2 中的匿名方法)仍然创建实际方法。将变量传递给这些方法会遇到一些难题(按值传递?按引用传递?C# 通过引用进行 - 但这会引发另一个问题,即引用可能比实际变量更有效)。C# 解决所有这些困境的方法是创建一个新的辅助类(“闭包”),其中的字段对应于 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 表达式有关——尽管循环的一些不小心使用证明了闭包陷阱。