闭包在 for 和 foreach 循环中的行为不同 [英] Closures behaving differently in for and foreach loops
问题描述
在 C# 中尝试闭包时,我发现如果它们在循环中捕获迭代器变量,它们会意外地工作.
While experimenting with closures in C# I found out that they work rather unexpectedly if they capture an iterator variable in a loop.
var actions = new List<Action>();
foreach (int i in new[] { 1, 2 })
actions.Add(() => Console.WriteLine(i));
for (int i = 3; i <= 4; i++)
actions.Add(() => Console.WriteLine(i));
foreach (var action in actions)
action();
上面的代码产生了一个奇怪的结果(我使用的是 .NET 4.5 编译器):
The above code produces a strange result (I'm using .NET 4.5 compiler):
1
2
5
5
为什么 i
的值在 2 个几乎相同的循环中的捕获方式不同?
Why is the value of i
captured differently for 2 almost identical loops?
推荐答案
在 C# 5 及更高版本中,foreach
循环声明了一个 separate i
循环每次迭代的变量.因此,每个闭包都捕获一个单独的变量,您会看到预期的结果.
In C# 5 and beyond, the foreach
loop declares a separate i
variable for each iteration of the loop. So each closure captures a separate variable, and you see the expected results.
在 for
循环中,你只有一个 single i
变量,它被所有的闭包捕获,并修改为循环进展 - 因此当您调用委托时,您会看到该单个变量的最终值.
In the for
loop, you only have a single i
variable, which is captured by all the closures, and modified as the loop progresses - so by the time you call the delegates, you see the final value of that single variable.
在 C# 2、3 和 4 中,foreach
循环的行为也是如此,这基本上从不所需的行为,因此它在 C# 5 中得到了修复.
In C# 2, 3 and 4, the foreach
loop behaved that way as well, which was basically never the desired behaviour, so it was fixed in C# 5.
在for
循环中,如果在循环体的范围内引入一个新的变量,也可以达到同样的效果:
You can achieve the same effect in the for
loop if you introduce a new variable within the scope of the loop body:
for (int i = 3; i <= 4; i++)
{
int copy = i;
actions.Add(() => Console.WriteLine(copy));
}
有关更多详细信息,请阅读 Eric Lippert 的博客文章关闭循环变量被认为有害" - 第 1 部分,第 2 部分.
For more details, read Eric Lippert's blog posts, "Closing over the loop variable considered harmful" - part 1, part 2.
这篇关于闭包在 for 和 foreach 循环中的行为不同的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!