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

时间:2013-05-28 21:36:19

标签: 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

4 个答案:

答案 0 :(得分:7)

这称为“访问修改后的闭包”。基本上,只有一个i变量,而且所有三个lambd都指的是它。最后,一个i变量已增加到3,因此所有三个操作都会打印3。 (请注意,lambda中的int loopScopedi = i仅在稍后调用lambda后才会运行。)

在第二个版本中,您为每次迭代创建一个新int loopScopedi,并将其设置为当前值i,即012,每次迭代。

您可以尝试想象内联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
}

答案 1 :(得分:2)

  

两者之间有什么区别?

范围不同。

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

在第二个循环中,您为每个 Action使用了一个新变量,因此您获得了012。

答案 2 :(得分:2)

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

在您的第一个示例中,i被提升一次并与for()和所有传递的lambdas一起使用。当您到达Console.WriteLine时,i已从3循环达到for()

在您的第二个示例中,每次循环运行时都会挂起一个新的loopScopedi,因此它不会受到后续循环的影响。

答案 3 :(得分:2)

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

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

我(几乎)有同样的问题,我了解了它here