【问题标题】:From Eric Lippert's blog: "don't close over the loop variable" [duplicate]来自 Eric Lippert 的博客:“不要关闭循环变量”[重复]
【发布时间】:2010-07-06 22:05:32
【问题描述】:

可能的重复:
Why is it bad to use a iteration variable in a lambda expression
C# - The foreach identifier and closures

来自Eric Lippert's 28 June 2010 入口:

static IEnumerable<IEnumerable<T>>
  CartesianProduct<T>(this IEnumerable<IEnumerable<T>> sequences)
{
  // base case:
  IEnumerable<IEnumerable<T>> result = new[] { Enumerable.Empty<T>() };

  foreach(var sequence in sequences)
  {
    var s = sequence; // don't close over the loop variable

    // recursive case: use SelectMany to build the new product out of the old one
    result = 
      from seq in result
      from item in s
      select seq.Concat(new[] {item});
  }

  return result;
}

var s = sequence; 看起来像一个空操作。为什么不是一个?直接使用sequence会出现什么问题?

而且,更主观地说:这在多大程度上被认为是 C# 行为的缺陷?

【问题讨论】:

标签: c# linq closures


【解决方案1】:

Eric 本人的几篇相关文章,以及 cmets 中的一些有趣讨论:

【讨论】:

    【解决方案2】:

    这是一个微妙的范围问题,与闭包和延迟执行的工作方式有关。

    如果您不使用局部变量,而是直接进行序列化,则结果 IEnumarable 绑定到 VARIABLE 序列而不是序列的 VALUE,并且在执行查询时,VARIABLE 序列包含的 LAST VALUE序列。

    如果您像 Eric 的示例中那样声明另一个局部变量,则范围仅限于每个循环迭代。因此,即使执行被推迟,它也会按预期进行评估。

    【讨论】:

      【解决方案3】:

      此处使用的 LINQ 查询导致 s 的值在它最初定义的范围之外可用(即,CartesianProduct 方法)。这就是所谓的closure。由于延迟执行,在实际评估 LINQ 查询时(假设最终评估),封闭方法将已完成,s 变量将“超出范围”,至少根据传统范围规则。尽管如此,在这种情况下引用s 仍然是“安全的”。

      在传统的函数式编程语言中,闭包非常方便且表现良好,其中事物自然是不可变的。 C# 最重要的是一种命令式编程语言,其中变量默认是可变的,这一事实是导致这种奇怪解决方法的问题的基础。

      通过在循环范围内创建中间变量,您实际上是在指示编译器为 LINQ 查询的每次迭代分配一个单独的非共享变量。否则,每次迭代都将共享相同的变量实例,这也将(显然)是相同的值......可能不是你想要的。

      【讨论】:

        【解决方案4】:

        那篇博文中的一位 cmets:

        但是你的第一个有一个错误 CartesianProduct 方法的版本 :你正在关闭循环 变量,因此,由于延迟 执行,它使笛卡尔 最后一个序列的乘积 本身。您需要添加一个临时 foreach 循环内的局部变量 使它工作(第二个版本 不过效果很好)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-28
          • 2011-08-16
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多