我遇到了一个关于 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 个 10。
似乎这是由于所有操作都引用了一个捕获的变量。结果,当它们被调用时,它们都具有相同的输出。
有没有办法绕过这个限制,让每个动作实例都有自己的捕获变量?
是的 - 在循环内获取变量的副本:
while (variable < 5) { int copy = variable; actions.Add(() => copy * 2); ++ variable; }
您可以将其想象为 C# 编译器每次遇到变量声明时都会创建一个“新”局部变量。事实上,它会创建适当的新闭包对象,如果您在多个范围内引用变量,它会变得复杂(在实现方面),但它可以工作:)
请注意,此问题更常见的情况是使用foror 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变化,因此您不再需要制作本地副本。