我有以下代码:
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
是一种扩展方法,但结果不是我提到的。为什么呢?
答案 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());
这应该正确地提供55
和10055
答案 1 :(得分:1)
通常,您可以假设只要Linq函数返回某个IEnumerable或IQueryable,就可能延迟执行。
当返回的值是TSource类型的一个项目或实现ICollection&lt; TSource&gt;您可以放心,执行不会延期(任何人都知道有任何例外吗?)
绝对确定:Enumerable函数的MSDN描述描述了该函数是否通过使用延迟执行来实现。
此方法通过使用延迟执行来实现。眼前的 返回值是存储所有信息的对象 需要执行操作。此方法表示的查询 在枚举对象之前不会执行...
使用延迟执行未实现函数Enumerable.Max。因此,如果在计算Max之后序列发生变化,则不会更改Max的结果。
答案 2 :(得分:1)
一些LINQ方法(如Where
和Select
)被推迟,因为计算一个结果与计算下一个结果无关。但并非所有在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中延迟,因为它们可以包装在允许延迟的对象中。