C#,闭包和Lambda

时间:2011-08-26 09:51:29

标签: .net c#-4.0 lambda c#-3.0 foreach

我不打算问什么是关闭。这是一个关闭: 例如:

List<Func<int>> add = new List<Func<int>>();

List<int> coll = new List<int>(){1,2,3,4,5};
foreach (int i in coll)
{
     add.Add(() => i*2);
}

由于闭包关闭变量,如果我们尝试调用“add”列表的所有Func,毫无疑问结果将是10。这让我想到,如果这是闭包,那么下面的例子也应该是一个闭包。

//Indirect way of writing the same example
Enumerable.Range(1, 5).ToList().ForEach(x => add.Add(() => x * 2));

这里我们也在关闭变量,因此变量的状态应该是变量的最后一个值,但事实证明,事实并非如此。这不是关闭。 lambda是否以不可变的方式构造其变量,即只要我们更改x的值,就会创建一个新变量来存储值?

3 个答案:

答案 0 :(得分:1)

不同之处在于,第一个示例是为每个委托共享同一个i实例,因为i在整个循环中被捕获一次。第二个示例为每个函数提供1..5的唯一值。

要使第一个示例工作相同,您可以在循环中使用局部变量,如下所示,现在为每个函数单独捕获x。

  foreach (int i in coll)
  {
    int x = i;
    add.Add(() => x * 2);
  }

<小时/> 添加信息

以下是Eric Lippert关于该主题的两篇文章

Closing over the loop variable considered harmful - Part 1

Closing over the loop variable considered harmful - Part 2

答案 1 :(得分:0)

在你的第二个例子中,每次调用add.Add时x都会改变 - 在你的第一个例子中,闭包中捕获了相同的变量“i”。

除此之外,您可以将.net中的闭包视为一个类对象,它捕获所有未在上下文中直接给出的“外部”数据。在您的第一个示例中,您可以考虑使用一个方法(用于执行i * 2)创建一个类,并考虑创建一个用于记住对象“i”的引用的字段。

答案 2 :(得分:0)

在你的第二个例子中,仍然会创建一个闭包,捕获变量x。变量x是每个调用的新变量。示威可能是有序的。

class SomeClass
{
    List<int> col = new List<int> {1,2,3,4,5};

    void SomeFunction()
    {
        for (int x = 0; x < 6; x++)
            ForEachFunction(x);
    }

    void ForEachFunction(int x) 
    {
        // x here is a copy of the variable from the for loop
        col.Add(x);
    }
}

然而,在您的第一个示例中,我之前已定义,并且每次调用都会重复使用。