c# - 在 C# 中的循环中捕获变量

标签 c# closures captured-variable

我遇到了一个关于 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。

这似乎是由于所有操作都引用了一个捕获的变量。因此,当它们被调用时,它们都有相同的输出。

有没有办法绕过这个限制,让每个 Action 实例都有自己的捕获变量?

最佳答案

是的 - 在循环中获取变量的副本:

while (variable < 5)
{
    int copy = variable;
    actions.Add(() => copy * 2);
    ++ variable;
}

您可以将其想象成 C# 编译器每次遇到变量声明时都会创建一个"new"局部变量。事实上,它会创建适当的新闭包对象,如果您在多个范围内引用变量,它会变得复杂(在实现方面),但它有效:)

请注意,此问题更常见的情况是使用 forforeach:

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 节,以及我的 article on closures还有更多的例子。

请注意,从 C# 5 编译器及更高版本开始(即使在指定 C# 的早期版本时),foreach 的行为已更改,因此您不再需要制作本地副本。参见 this answer了解更多详情。

关于c# - 在 C# 中的循环中捕获变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30198420/

相关文章:

c# - 在 C# 中的循环中捕获变量

c# - 将两个正则表达式组合成一个键/值对对象?

c# - 长列表选择器跳转列表屏幕windows phone

r - 改变闭包中的变量

c# - 是否可以加快单元测试的时间?

javascript - 这是 Javascript Closure 用例吗?

python - exec 不从闭包中获取变量

c# - 具有泛型的C#类设计

c# - 如何将动态对象传递给 NUnit TestCase 函数?