问题 c#在Lambda表达式中声明变量


下面的代码输出33而不是012.我不明白为什么在每次迭代中没有捕获新变量loopScopedi而不是捕获相同的变量。

Action[] actions = new Action[3];

for (int i = 0; i < 3; i++)
{

   actions [i] = () => {int loopScopedi = i; Console.Write (loopScopedi);};
}

foreach (Action a in actions) a();     // 333

Hopwever,这段代码产生012.两者之间有什么区别?

Action[] actions = new Action[3];

for (int i = 0; i < 3; i++)
{
    int loopScopedi = i;
    actions [i] = () => Console.Write (loopScopedi);
}

foreach (Action a in actions) a();     // 012

8435
2018-05-28 21:36


起源

啊这是一个修改后的关闭问题 - 请参阅 stackoverflow.com/questions/235455/access-to-modified-closure - Matt Roberts
关闭问题: codethinked.com/c-closures-explained 例如。行为在.net <4.5和> = 4.5中有所不同 - Raphaël Althaus
阅读Eric Lippert的这两个条目 blogs.msdn.com/b/ericlippert/archive/2009/11/16/... 和 blogs.msdn.com/b/ericlippert/archive/2009/11/12/... - Icarus


答案:


这称为“访问修改后的闭包”。基本上,只有一个 i 变量,所有三个lambdas都指它。最后,一个 i 变量已递增到 3,所以三个动作打印出来 3。 (注意 int loopScopedi = i 在lambda中只有在你稍后调用lambda后才会运行。)

在第二个版本中,您正在创建一个新版本 int loopScopedi 对于每次迭代,并将其设置为当前值 i,是的 0 和 1 和 2,每次迭代。

您可以尝试想象内联lambda以更清楚地了解正在发生的事情:

foreach (Action a in actions)
{
   int loopScopedi = i; // i == 3, since this is after the for loop
   Console.Write(loopScopedi); // always outputs 3
}

与:

foreach (Action a in actions)
{
   // normally you could not refer to loopScopedi here, but the lambda lets you
   // you have "captured" a reference to the loopScopedi variables in the lambda
   // there are three loopScopedis that each saved a different value of i
   // at the time that it was allocated
   Console.Write(loopScopedi); // outputs 0, 1, 2
}

7
2018-05-28 21:43





在lambda中捕获的变量被提升到lambda和外部代码之间共享的类中。

在你的第一个例子中, i 正在悬挂一次并与两者同时使用 for() 和所有通过的lambdas。当你到达时 Console.WriteLinei 已达到 3 来自 for() 循环。

在你的第二个例子中,一个新的 loopScopedi 每次循环运行时都会被提升,因此它不会受到后续循环的影响。


2
2018-05-28 21:45





这是关于C#如何处理闭包。在第一个示例中,将无法正确捕获闭包,您将最终使用最后一个值;但在第二个示例中,您将在占位符中捕获循环变量的当前值,然后使用该占位符;这提供了正确的解决方案。

C#捕获foreach循环中的循环变量和C#5.0以及之前版本中的循环之间存在差异 - 这是一个重大变化。

我(几乎)有同样的问题而且我了解了它 这里


2
2018-05-28 21:47





两者有什么区别?

范围不同。

在你的第一个循环中,你指的是 i 变量,在。中定义 for 循环语句范围,而在第二个循环中,您使用的是局部变量。 333输出的结果是你的第一个循环迭代3次,因此 i 变量最终增加到3,然后当你调用动作时,它们都引用了 相同 变量(i)。

在第二个循环中,您使用的是新变量 为每个人  Action 所以你得到012。


2
2018-05-28 21:39