C#Closure有效吗?

时间:2012-03-26 14:22:25

标签: c# closures

  

可能重复:
  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中,我经常遇到封闭,但在上面的代码中,为什么会有封闭? THX。

2 个答案:

答案 0 :(得分:6)

在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,这会导致此共享行为。

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

  

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

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

答案 1 :(得分:2)

关键是,当您在foreach循环中创建委托时,您将在循环变量 x上创建闭包,不是其当前值

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