应该't sum方法在LINQ中延迟
本文关键字:LINQ 延迟 方法 sum 应该 | 更新日期: 2023-09-27 18:02:51
我有以下代码:
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
我在这里看到的是关于延迟求值的,但没有帮助。Sum
是一种扩展方法,但结果不是我所提到的。为什么?
只有返回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());
这应该正确地给出55
和10055
一般来说,你可以假设只要一个Linq函数返回一个IEnumerable或IQueryable对象,那么它的执行就可能被延迟。
当返回值是TSource类型的一项或实现了iccollection
绝对确定:Enumerable函数的MSDN描述描述了该函数是否通过延迟执行实现。
例如Enumerable。选择:
这个方法是通过延迟执行来实现的。最直接的返回值是一个对象,它存储所有的信息执行操作所必需的。此方法表示的查询在对象被枚举之前不会执行…
Enumerable函数。Max不是通过延迟执行来实现的。因此,如果在计算Max后序列发生变化,则Max的结果不会改变。
参见Stackoverflow: Linq函数何时延迟?
一些LINQ方法(如Where
和Select
)被延迟,因为计算一个结果与计算下一个结果是独立的。但是并不是所有操作IEnumerable<T>
的方法都必须被延迟。
例如,Sum
将把序列的所有元素约简为一个。因此,它可以不计算任何东西,也可以计算所有东西,但没有办法在两者之间做任何事情。它的作者选择打破他们通常的LINQ习惯,他们让它快速计算而不是懒惰计算。
这可以通过以下事实证明:IEnumerable<int>
上的Sum
有一个返回类型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
返回IEnumerable的函数可以在Linq中被延迟,因为它们可以被包装在一个允许延迟的对象中。