多次读取IEnumerable

本文关键字:IEnumerable 读取 | 更新日期: 2023-09-27 18:10:12

假设我有一些代码:

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

多次读取IEnumerable

嗯,这取决于你想做什么。您可以执行两次查询(其确切含义将取决于GetAllItems()的操作),您可以将结果复制到列表中:

var items = ItemsGetter.GetAllItems().Where(x => x.SomeProperty > 20).ToList();

一旦它在列表中,显然迭代该列表多次就不是问题了。

注意你不能调用Reset,因为你没有迭代器——你有IEnumerable<T>。我不建议在一般情况下调用IEnumerator<T>——许多实现(包括c#编译器从迭代器块生成的任何实现)实际上并没有实现Reset(即它们抛出异常)。

我偶尔会遇到必须多次处理一个可枚举数的情况。如果枚举是昂贵的,不可重复的,并产生大量的数据(如从数据库中读取的IQueryable),枚举多次不是一个选项,也不是在内存中缓冲结果。

直到今天,我经常以编写聚合器类而告终,我可以在foreach循环中推入项并最终读取结果——远不如LINQ优雅。

等等,我刚才说的是"push"吗?这听起来不像…反应吗?所以我在想今晚散步的时候。回到家,我试了试——效果很好!

示例代码段显示了如何使用标准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();
}

LINQ使用延迟执行,所以'items'只会在你通过另一个方法请求时枚举。每个Sum方法都需要O(n)次迭代。根据项目列表的大小,您可能不希望对其进行多次迭代。