2016-02-12 66 views
4

在C#中试用闭包时,我发现如果它们在循环中捕获迭代器变量,它们会意外地工作。在for和foreach循环中闭包的行为不同

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编译器):

1 
2 
5 
5 

为什么的i值捕获不同2个几乎相同的循环?

+0

你注意到这个微妙的区别是聪明的。是的,这里有一个危险,有人可以重构一个foreach到一个for,并没有意识到他们正在引入语义变化!我很有兴趣知道,如果您对“for”案件有“真实世界”使用案例,特别是“for”奇怪行为是期望行为的案例。 –

回答

7

在C#5和超越,foreach环声明了循环的每次迭代一个单独i变量。所以每个闭包都会捕获一个单独的变量,并且您会看到预期的结果。

for循环,你只有一个i变量,它是由所有的封闭拍摄,并且修改循环的进展 - 所以,当你调用代表的时候,你看到的最终值单变量。

在C#2,3和4所示,foreach环路表现这种方式为好,这是基本上从未所期望的行为,所以它是固定在C#5

可以实现相同的效果在for循环,如果你的循环体的范围内引入新的变量:

for (int i = 3; i <= 4; i++) 
{ 
    int copy = i; 
    actions.Add(() => Console.WriteLine(copy)); 
} 

欲了解更多详情,请阅读埃里克利珀的博客文章,“关闭了被认为是有害的循环变量” - part 1part 2

0

在foreach情况下,它在本地变量中保存值,所以它在每个委托中都有它自己的值,而在for循环情况下则不是这样,在for循环情况下,所有委托都指向相同的i,所有代表均使用i中更新的最后一个值。

这是在foreach循环的情况下的一个突破性变化,在旧版本中它们都曾以相同的方式工作。