C#5.0中捕获的闭包(循环变量)

Kav*_*ian 46 c# foreach closures for-loop 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();
Run Code Online (Sandbox Code Playgroud)

打印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();
Run Code Online (Sandbox Code Playgroud)

问题:这是我们在5.0之前的C#版本中遇到的问题; 所以我们不得不使用一个循环局部占位符来进行闭包,它现在已经修复了 - 在C#5.0中 - 在"foreach"循环中.但不是"for"循环!

这背后的原因是什么(也没有解决for循环问题)?

Jon*_*eet 44

这背后的原因是什么?

我会假设你的意思是"为什么for循环也没有改变?"

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

  • 初始化
  • 条件
  • 迭代器
  • 身体

...然后循环大致是:

{
    initializer;
    while (condition)
    {
        body;
        iterator;
    }
}
Run Code Online (Sandbox Code Playgroud)

(当然,除了iteratorcontinue;声明结尾处执行.)

初始化部分逻辑上只发生一次,因此只有一个"变量实例化"完全符合逻辑.此外,有一个在每次循环迭代变量的不自然的"初始"值-有什么可说的,一个for循环已经成为一种形式,初始化变量声明,在条件测试,并在迭代器修改它.您期望像这样的循环做什么:

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

将它与一个foreach循环相比较,看起来你正在为每次迭代声明一个单独的变量.哎呀,变量是只读的,这使得它奇怪地认为它是一个在迭代之间变化的变量.将foreach循环视为在每次迭代中声明一个新的只读变量并将其值从迭代器中获取是完全合理的.

  • (我很难与一位高级开发人员在他的照片旁边以559k争辩;然而:)常识(限制在我脑海中的部分)往往不同意,而在许多其他语言中你可以看到for and foreach.让我们用这种方式重写while循环:var x = initializer(); while(condition){feed_x_to_body(body); x = iterator(); 并且我希望feed_x_to_body(这是编译器的工作)按预期完成它的工作(而不是根据底层实现的逻辑 - 编译器的兴趣不是我!).也许我错了; 请指导更多. (6认同)
  • @KavehShahbazian:好吧,简单地说“它没有”!在 C# 中,捕获的是 *variable*,而不是它的值。所以 lambda 表达式可以改变变量,而从 lambda 表达式外部对变量的更改可以在 lambda 表达式内看到。 (3认同)
  • @KavehShahbazian:不,这不是`for` 循环的工作方式。许多 `for` 循环 * 发生 * 在初始化程序中声明一个变量是无关紧要的。在初始值设定项中声明的变量*是*循环体中使用的变量-并不是循环体被赋予了变量*值*。特别是,循环体也可以*修改*变量,这会破坏你的世界模型。 (2认同)