这与循环无关。
此行为是由于在 lambda 表达式 () => variable * 2
中使用了外部作用域中未在 lambda 的内部作用域中定义的变量 variable
所触发的。
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;
actions.Add(() => t * 2);
++variable;
}
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表达式使用本地范围变量有关-尽管一些不谨慎使用循环的情况会展示闭包陷阱。