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

Posted

技术标签:

【中文标题】c# 在 Lambda 表达式中声明变量【英文标题】:c# declaring variables inside Lambda expressions 【发布时间】:2013-05-24 00:07:57 【问题描述】:

以下代码输出 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

但是,这段代码生成 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

【问题讨论】:

啊,这是一个修改后的关闭问题 - 请参阅 ***.com/questions/235455/access-to-modified-closure 关闭问题:codethinked.com/c-closures-explained 例如。 .net = 4.5 中的行为不同 阅读 Eric Lippert blogs.msdn.com/b/ericlippert/archive/2009/11/16/… 和 blogs.msdn.com/b/ericlippert/archive/2009/11/12/… 的这两个条目 【参考方案1】:

这称为“访问修改后的闭包”。基本上,只有一个 i 变量,所有三个 lambdas 都在引用它。最后,一个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

【讨论】:

【参考方案2】:

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

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

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

【讨论】:

【参考方案3】:

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

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

我(几乎)有同样的问题,我知道了here。

【讨论】:

【参考方案4】:

两者有什么区别?

范围不同。

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

在第二个循环中,您使用一个新变量 for each Action,所以您会得到 012。

【讨论】:

以上是关于c# 在 Lambda 表达式中声明变量的主要内容,如果未能解决你的问题,请参考以下文章

lambda表达式传参

C# 委托 —— 委托 泛型委托与Lambda表达式

C# Lambda表达式

如何使用lambda表达式捕获局部变量?

关于 C# 闭包

C# 10 Lambda 语法的改进