我遇到了一个关于 C# 的有趣问题。我有如下代码。列表
我遇到了一个有关 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。但是,它实际上输出了五个 10。
看起来这是因为所有操作都引用一个捕获的变量。因此,当它们被调用时,它们都有相同的输出。
有没有办法解决这个限制,让每个动作实例都有自己的捕获变量?
是的——复制循环内的变量:
while (variable < 5)
{
int copy = variable;
actions.Add(() => copy * 2);
++ variable;
}
您可以认为 C# 编译器每次遇到变量声明时都会创建一个“新”局部变量。实际上,它会创建适当的新闭包对象,如果您在多个范围内引用变量,它会变得复杂(就实现而言),但它确实有效 :)
请注意,此问题更常见的情况是使用 f或
or 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
变化,因此您不再需要进行本地复制。 有关更多详细信息, 此答案