下面的代码输出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
这称为“访问修改后的闭包”。基本上,只有一个 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
}
在lambda中捕获的变量被提升到lambda和外部代码之间共享的类中。
在你的第一个例子中, i
正在悬挂一次并与两者同时使用 for()
和所有通过的lambdas。当你到达时 Console.WriteLine
, i
已达到 3
来自 for()
循环。
在你的第二个例子中,一个新的 loopScopedi
每次循环运行时都会被提升,因此它不会受到后续循环的影响。
这是关于C#如何处理闭包。在第一个示例中,将无法正确捕获闭包,您将最终使用最后一个值;但在第二个示例中,您将在占位符中捕获循环变量的当前值,然后使用该占位符;这提供了正确的解决方案。
C#捕获foreach循环中的循环变量和C#5.0以及之前版本中的循环之间存在差异 - 这是一个重大变化。
我(几乎)有同样的问题而且我了解了它 这里。
两者有什么区别?
范围不同。
在你的第一个循环中,你指的是 i
变量,在。中定义 for
循环语句范围,而在第二个循环中,您使用的是局部变量。 333输出的结果是你的第一个循环迭代3次,因此 i
变量最终增加到3,然后当你调用动作时,它们都引用了 相同 变量(i
)。
在第二个循环中,您使用的是新变量 为每个人 Action
所以你得到012。