【问题标题】:Shouldn't sum method be deferred in LINQ不应该在 LINQ 中延迟 sum 方法
【发布时间】:2016-08-18 11:52:00
【问题描述】:

我有以下代码:

List<int> no = new List<int>() { 1, 2, 3, 4, 5 };
var res2 = no.Sum(a => a * a);
Console.WriteLine(res2);
no.Add(100);
Console.WriteLine(res2);

我希望得到以下结果:

55
10055

但两人都是 55 岁

55
55

我见过here 是关于延迟评估的,但没有帮助。 Sum 是扩展方法,但结果不是我说的。为什么?

【问题讨论】:

  • 您期望的结果是什么?
  • 那么您期望得到什么结果?
  • Sum 不会被推迟。只有返回 IEnumerable 的方法会被延迟。
  • 问题是什么?你期望得到什么,你得到了什么?

标签: c# linq


【解决方案1】:

只有返回 IEnumerable&lt;T&gt; 的函数才能在 Linq 中延迟(因为它们可以包装在允许延迟的对象中)。

Sum 的结果是int,所以它不可能以任何有意义的方式推迟它:

var res2 = no.Sum(a => a * a);
// res2 is now an integer with a value of 55
Console.WriteLine(res2);
no.Add(100);

// how are you expecting an integer to change its value here?
Console.WriteLine(res2);

您可以推迟执行(不是真正推迟,而是显式调用它),例如,通过将 lambda 分配给 Func&lt;T&gt;

List<int> no = new List<int>() { 1, 2, 3, 4, 5 };
Func<int> res2 = () => no.Sum(a => a * a);
Console.WriteLine(res2());
no.Add(100);
Console.WriteLine(res2());

这应该正确地给出5510055

【讨论】:

    【解决方案2】:

    通常你可以假设只要一个 Linq 函数返回一个 IEnumerable 或 IQueryable 的东西,那么执行可能会被推迟。

    当返回值是 TSource 类型的一项或实现 ICollection 的对象时,您可以确保执行不会延迟(任何人都知道任何异常?)

    绝对肯定:MSDN 对 Enumerable 函数的描述描述了该函数是否使用延迟执行来实现。

    例如Enumerable.Select:

    这个方法是通过延迟执行来实现的。直接的 返回值是一个存储所有信息的对象 执行该操作所需的。此方法表示的查询 在枚举对象之前不会执行...

    函数Enumerable.Max 未使用延迟执行实现。所以如果你计算了Max之后序列发生了变化,Max的结果不会改变。

    另见Stackoverflow: when is a Linq function deferred?

    【讨论】:

      【解决方案3】:

      一些 LINQ 方法(如 WhereSelect)被推迟,因为计算一个结果独立于计算下一个结果。但并非所有在 IEnumerable&lt;T&gt; 上运行的方法都必须延迟。

      例如,Sum 会将序列中的所有元素归为一个。因此,它什么都不能计算或什么都不能计算,但中间没有办法做任何事情。它的作者选择打破他们惯常的 LINQ 习惯,并让它急切地而不是懒惰地计算。

      IEnumerable&lt;int&gt; 上的 Sum 的返回类型为 int,这是一个已计算的整数,这一事实证明了这一点:

      int res2 = no.Sum(a => a * a);
      

      如果你想推迟Sum的计算,有一个简单的方法——使用Func&lt;int&gt;

      Func<int> res2 = () => no.Sum(a => a * a);
      

      或者,您可以将其设为类似 LINQ 的扩展方法:

      public static Func<int> LazySum(this IEnumerable<int> sequence, Func<int, int> selector)
              => () => sequence.Sum(selector);
      

      然后像这样使用它:

      var res2 = no.LazySum(a => a * a);
      

      无论您选择哪一个,您都可以验证它是否会给您延迟计算:

      Console.WriteLine(res2()); // prints 55
      no.Add(100);
      Console.WriteLine(res2()); // prints 10055
      

      【讨论】:

        【解决方案4】:

        返回 IEnumerable 的函数可以在 Linq 中延迟,因为它们可以包装在允许延迟的对象中。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-06-13
          • 1970-01-01
          • 2017-08-14
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多