2013-04-28 42 views
39

这工作正常(意味着如预期)在C#5.0:捕获封闭(循环变量)在C#5.0

var actions = new List<Action>(); 
foreach (var i in Enumerable.Range(0, 10)) 
{ 
    actions.Add(() => Console.WriteLine(i)); 
} 
foreach (var act in actions) act(); 

打印0到9但这一个示出了10 10次:

var actions = new List<Action>(); 
for (var i = 0; i < 10; i++) 
{ 
    actions.Add(() => Console.WriteLine(i)); 
} 
foreach (var act in actions) act(); 

问题:这是我们在5.0之前的C#版本中遇到的问题;所以我们必须为闭包使用循环局部占位符,并且现在在C#5.0中已经修复 - 在“foreach”循环中。但不是在“for”循环中!

这是什么原因(不解决for循环的问题)?

+2

你的意思是“它为什么不被固定for'循环以及”? – 2013-04-28 15:15:49

+0

我很惊讶它甚至在第一种情况下工作...因为您在退出foreach/for的范围后运行该操作。 'var i'不应该存在了。对我而言,这是一个非常“危险”的设计。 – LightStriker 2013-04-28 15:17:13

+0

@LightStriker:不;这是一个功能。这就是所谓的封闭。 – SLaks 2013-04-28 15:18:28

回答

37

这是什么原因?

我打算假定你的意思是“为什么for循环不变?”

答案是对于for循环,现有的行为非常有意义。如果你打破了for循环到:

  • 初始化
  • 条件
  • 迭代

...则循环大致是:

{ 
    initializer; 
    while (condition) 
    { 
     body; 
     iterator; 
    } 
} 

(除了执行iterator a T A continue;语句的结束为好,当然。)

初始化部分逻辑只会发生一次,所以这是完全合乎逻辑的,只有一个“变量实例”。此外,在循环的每次迭代中没有自然的“初始”变量值 - 没有什么可说的,for循环具有的形式是在初始化程序中声明变量的形式,在条件中测试它并修改它在迭代器中。你会得到什么像这样的循环来:

for (int i = 0, j = 10; i < j; i++) 
{ 
    if (someCondition) 
    { 
     j++; 
    } 
    actions.Add(() => Console.WriteLine(i, j)); 
} 

相比之下,与一个foreach环路看起来像你宣布每次迭代一个独立的变量。哎呀,这个变量是只读的,使得它甚至可以认为它是一个在迭代之间变化的变量。把一个foreach循环想象成在每次迭代中声明一个新的只读变量,并从迭代器中获取其值,这是非常有意义的。

+5

(这是我很难以高顺位开发商争论在他的照片旁边有559k;但是:)常识(限于我的思维范围的部分)倾向于不同意,并且在许多其他语言中,您可以看到对于和foreach的行为。让我们用这种方式重写while循环:var x = initializer(); while(condition){feed_x_to_body(body); x = iterator(); }我期望feed_x_to_body(这是编译器的工作)能够按预期工作(而不是根据底层实现的逻辑 - 这是编译器的兴趣不是我!)。也许我错了;请多指导。 – 2013-04-28 15:48:33

+0

@KavehShahbazian:不,那不是'for'循环的工作原理。许多'for'循环*发生*在初始化器中声明一个变量是无关紧要的。在初始化程序*中声明的变量是在循环体中使用的变量 - 它不是赋给变量*值*。特别是,循环的主体也可以修改变量,这会破坏你的世界模型。 – 2013-04-28 15:51:57

+0

“能够修改变量”对我来说很清楚。然而,我认为当我创建一个新的范围(如lambda体)时,变量的值应该与它的主范围分离。这部分仍然没有发现它在我的脑海中,你会很乐意引导更多关于那部分。但我将你的回答标记为答案,因为它符合(当前)C#世界。谢谢; – 2013-04-28 16:00:33