C# 闭包有效吗? [复制]

Posted

技术标签:

【中文标题】C# 闭包有效吗? [复制]【英文标题】:C# Closure works? [duplicate] 【发布时间】:2012-04-10 01:30:25 【问题描述】:

可能重复:Is there a reason for C#'s reuse of the variable in a foreach?Looping through a list of Actions

今天我遇到了一个关于 C# foreach 函数的问题,它没有给我预期的正确结果。这是代码:

using System;
using System.Collections.Generic;

namespace ConsoleApplication1

class Program

    static void Main(string[] args)
    
        int[] data = new int[]  1, 2, 3, 4, 5 ;
        List<Func<int>> actions = new List<Func<int>>();
        foreach (int x in data)
        
            actions.Add(delegate()  return x; );
        
        foreach (var foo in actions)
        
            Console.WriteLine(foo());   
        
        Console.ReadKey();
    


当我在控制台应用程序中运行它时,它在屏幕上打印了五个 5。为什么?我只是无法理解。问了一些人,他们只是说这段代码中有闭包,但是我不是很清楚,我记得在javascript中我经常遇到闭包,但是在上面的代码中,为什么会有闭包?谢谢。

【问题讨论】:

看看:***.com/questions/9569334/… blogs.msdn.com/b/ericlippert/archive/2009/11/12/… 这个问题几乎每天都会被问到。请参阅***.com/questions/8898925/… 以获得对该问题的良好讨论。 【参考方案1】:

在 C#4 中,foreach 循环的所有迭代共享相同的变量,因此也共享相同的闭包。

规范说:

foreach (V v in x) embedded-statement 

然后扩展为:

  
  E e = ((C)(x)).GetEnumerator(); 
  try
   
    V v; 
    while (e.MoveNext())
     
      v = (V)(T)e.Current; 

      embedded-statement 
     
   
  finally
    
    … // Dispose e 
   

您可以看到v 是在while-loop 之外的块中声明的,这导致了这种共享行为。

这可能会在 C#5 中改变。

我们正在进行重大更改。在 C# 5 中,foreach 的循环变量在逻辑上将位于循环内部,因此闭包每次都会关闭变量的新副本。 “for”循环不会改变。

http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx

【讨论】:

为什么这个早期的规范说 v 在 while 循环中声明 msdn.microsoft.com/en-GB/library/aa664754.aspx【参考方案2】:

关键是,当您在 foreach 循环中创建委托时,您正在为循环创建一个闭包变量 x不是它的当前值 .

只有当你执行actions中的委托时才会确定值,也就是x当时的值。由于您已经完成了 foreach 循环,因此该值将是您的 data 数组中的最后一项,即 5。

【讨论】:

+1:非常清晰简洁的解释。 请务必注意,最后一句仅适用于 C#4 及更早版本。 ..正如你已经指出的那样,所以我不想重复它;-)

以上是关于C# 闭包有效吗? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

这个对象生命周期扩展闭包是 C# 编译器错误吗?

我可以使用原型模式而不是闭包模式来创建解析为对象的承诺吗? [复制]

C#列表添加有效但不能追加? [复制]

thisapply/callbind闭包函数变量复制

什么时候 ArrayList 比在 C# 中使用数组更有效? [复制]

为啥强制转换 SQL MAX(DateTime) 命令在 SSMS 中有效,但在 C# 中无效? [复制]