假设我有一些代码:
var items = ItemsGetter.GetAllItems().Where(x => x.SomeProperty > 20);
int sum1 = items.Sum(x => x.SomeFlag == true);
例如,我需要在代码后面的items集合中获得一些其他的总和。
int sum2 = items.Sum(x => x.OtherFlag == false);
所以我的问题:在IEnumerable
多次调用Linq方法是否可以?也许我应该在枚举器上调用Reset()
方法,或者使用ToList
方法从项目中创建列表?
答案 0 :(得分:21)
嗯,这真的取决于你想做什么。您可以执行两次执行查询的命中(其确切含义取决于GetAllItems()
的作用),或您可以将结果复制到列表:
var items = ItemsGetter.GetAllItems().Where(x => x.SomeProperty > 20).ToList();
一旦它出现在列表中,显然多次迭代该列表并不是一个问题。
请注意,您无法调用Reset
,因为您没有迭代器 - 您拥有IEnumerable<T>
。我不建议总体上调用IEnumerator<T>
- 许多实现(包括由迭代器块中的C#编译器生成的任何实现)实际上并不实际实现Reset
(即它们抛出异常)。
答案 1 :(得分:3)
我偶尔会遇到多次处理可枚举的情况。如果枚举是昂贵的,不可重复的并且产生大量数据(如从数据库读取的IQueryable),枚举多次不是一种选择,也不是在内存中缓冲结果。
直到今天,我经常最终编写聚合器类,我可以在foreach循环中推送项目并最终读取结果 - 比LINQ更不优雅。
但等等,我只是说&#34;推&#34;?听起来不像是......被动吗?所以我在今晚的行走中思考。回到家我尝试了 - 它有效!
示例代码段显示了如何使用标准LINQ运算符(即Rx的运算符)在单个传递中从一系列整数中获取最小和最大项:
public static MinMax GetMinMax(IEnumerable<int> source)
{
// convert source to an observable that does not enumerate (yet) when subscribed to
var connectable = source.ToObservable(Scheduler.Immediate).Publish();
// set up multiple consumers
var minimum = connectable.Min();
var maximum = connectable.Max();
// combine into final result
var final = minimum.CombineLatest(maximum, (min, max) => new MinMax { Min = min, Max = max });
// make final subscribe to consumers, which in turn subscribe to the connectable observable
var resultAsync = final.GetAwaiter();
// now that everybody is listening, enumerate!
connectable.Connect();
// result available now
return resultAsync.GetResult();
}
答案 2 :(得分:1)
LINQ使用延迟执行,因此'items'只会在您通过其他方法请求时枚举。每个Sum方法都需要O(n)迭代。根据项目列表的大小,您可能不希望多次迭代它。