不应该在LINQ

时间:2016-08-18 11:52:00

标签: c# linq

我有以下代码:

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是一种扩展方法,但结果不是我提到的。为什么呢?

4 个答案:

答案 0 :(得分:8)

只有返回IEnumerable<T>的函数才能在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<T>来延迟执行(不是真正推迟,而是显式调用它):

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

答案 1 :(得分:1)

通常,您可以假设只要Linq函数返回某个IEnumerable或IQueryable,就可能延迟执行。

当返回的值是TSource类型的一个项目或实现ICollection&lt; TSource&gt;您可以放心,执行不会延期(任何人都知道有任何例外吗?)

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

例如Enumerable.Select:

  

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

使用延迟执行未实现函数Enumerable.Max。因此,如果在计算Max之后序列发生变化,则不会更改Max的结果。

另见Stackoverflow: when is a Linq function deferred?

答案 2 :(得分:1)

一些LINQ方法(如WhereSelect)被推迟,因为计算一个结果与计算下一个结果无关。但并非所有在IEnumerable<T>上运行的方法都必须推迟。

例如,Sum会将序列的所有元素都减少为一个。因此,它可以根本不计算任何东西,但是没有办法在它们之间做任何事情。它的作者选择打破他们通常的LINQ习惯,他们急切地计算它而不是懒惰。

事实证明,Sum上的IEnumerable<int>的返回类型为int,这是一个已经计算过的整数:

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

如果您想推迟Sum的计算,可以采用一种简单的方法 - 使用Func<int>

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

答案 3 :(得分:1)

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