【问题标题】:C#, Closures and LambdasC#、闭包和 Lambda
【发布时间】:2011-08-26 09:51:29
【问题描述】:

我不会问什么是闭包。这是一个闭包: 例如:

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 的值,就会创建一个新变量来存储该值?

【问题讨论】:

  • 记住,变量是一个存储位置。您不会期望在两次不同的 lambda 调用中形式参数 x 使用相同的存储位置,因为对应于 x 的两个参数可能同时不同。调用方法会为形参和局部变量创建新的存储位置;这些存储位置包含在封闭中。您的第一个示例中的“i”目前只有一个存储位置。 (我们可能会在下一个版本中更改它,因为这是一个常见错误。)
  • 我不知道这还会被问多少次......

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


【解决方案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

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

      在您的第二个示例中,仍然创建了一个闭包,用于捕获变量 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);
          }
      }
      

      然而,在您的第一个示例中,i 是之前定义的,并且在每次调用时都被重用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-04-01
        • 2020-09-01
        • 2016-06-16
        • 1970-01-01
        • 2018-08-08
        • 2012-03-24
        • 1970-01-01
        相关资源
        最近更新 更多