我遇到了一个有关C#的有趣问题。我有下面的代码。
List<Func<int>> actions = new List<Func<int>>(); int variable = 0; while (variable < 5) { actions.Add(() => variable * 2); ++ variable; } foreach (var act in actions) { Console.WriteLine(act.Invoke()); }
我希望它输出0、2、4、6、8。但是,它实际上输出5个10s。
似乎这是由于所有操作都引用了一个捕获的变量。结果,当它们被调用时,它们都具有相同的输出。
有没有办法解决这个限制,让每个动作实例都有自己的捕获变量?
是的-在循环中获取变量的副本:
while (variable < 5) { int copy = variable; actions.Add(() => copy * 2); ++ variable; }
您可以认为它就像C#编译器每次击中变量声明时都创建一个“新”局部变量一样。实际上,它将创建适当的新闭包对象,并且如果您在多个作用域中引用变量,它会变得复杂(就实现而言),但是它是可行的:)
请注意,此问题更常见的情况是使用for或foreach:
for
foreach
for (int i=0; i < 10; i++) // Just one variable foreach (string x in foo) // And again, despite how it reads out loud
有关此操作的更多详细信息,请参见C#3.0规范的7.14.4.2节,我有关闭包的文章也提供了更多示例。
请注意,从C#5编译器及更高版本开始(即使指定了C#的早期版本),其行为也已foreach更改,因此您不再需要进行本地复制。有关更多详细信息,请参见此答案。