c#闭包工作方法?

C# Closure works?

本问题已经有最佳答案,请猛点这里访问。

Possible Duplicate:
Is there a reason for C#'s reuse of the variable in a foreach?
Looping through a list of Actions

今天我遇到了一个关于c foreach函数的问题,它没有像我预期的那样给出正确的结果。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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个5。为什么?我就是不明白。有人问过一些人,他们只是说这段代码中有闭包,但我对此不是很清楚,我记得在javascript中,我经常遇到闭包,但在上面的代码中,为什么会有闭包?谢谢。


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

说明书上说:

1
foreach (V v in x) embedded-statement

is then expanded to:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{  
  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中改变。

We are taking the breaking change. In C# 5, the loop variable of a foreach will be logically inside the loop, and therefore closures will close over a fresh copy of the variable each time. The"for" loop will not be changed.

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


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

只有在执行actions中的委托时,才会确定该值,即当时x的值。既然您已经完成了foreach循环,那么该值将是data数组中的最后一项,即5。