多次读取IEnumerable

时间:2011-09-30 15:13:07

标签: c# linq ienumerable

假设我有一些代码:

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方法从项目中创建列表?

3 个答案:

答案 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)迭代。根据项目列表的大小,您可能不希望多次迭代它。