c#,用多个. where()语句过滤IEnumerable,性能提升

本文关键字:IEnumerable 过滤 性能 语句 where | 更新日期: 2023-09-27 18:03:20

我有一个IEnumerable,我通过. where表达式应用多个过滤器。我的代码看起来像这样

public List<MyObject> FilteringFunction(List<MyObject> listToFilter, List<Filter> filters)
{
    // A dirty way to have an enumerable instead of List
    var enumerableToFilter = listToFilter.Where(x => true);
    foeach(var filter in filters)
    {
        enumerableToFilter = enumerableToFilter.Where(x => x.Value.Contains(filter.Value));
    }
    return enumerableToFilter.ToList();
}

我是否只迭代我的集合一次?(因为我只有一个数据库调用与LINQ到SQL)

c#,用多个. where()语句过滤IEnumerable,性能提升

Enumerables延迟执行,直到您遍历它们,并且在通过集合的单个迭代上应用多个过滤器。与其他linq语句结合可能会强制提前枚举,我没有测试每一个组合。这只会在非常大的数据集或低规格性能关键系统上出现问题。

下面是一个使用Visual studio c#交互的例子

> class Item
. {
.     private int _number;
.     public int Number
.     {
.         get { Console.WriteLine($"Got number {_number}"); return _number; }
.         set { _number = value; }
.     }
. }
> 
> IEnumerable<Item> items = new List<Item>() { 
.     new Item { Number = 1 },
.     new Item { Number = 2 },
.     new Item { Number = 3 },
.     new Item { Number = 4 },
.     new Item { Number = 5 },
.     new Item { Number = 6 }
. };
> 
> var filteredItems = items.Where(item => item.Number > 3).Where(item => item.Number % 2 == 0);
> 
> var listedItems = filteredItems.ToList();
Got number 1
Got number 2
Got number 3
Got number 4
Got number 4
Got number 5
Got number 5
Got number 6
Got number 6
> 

注意,1、2和3被过滤掉了,并且没有对它们调用第二个过滤方法。4、5和6都通过第一个过滤器,因此两个过滤器都被应用。

要点:注意,在可枚举对象被读入列表之前,过滤实际上不会发生。您将能够继续添加过滤器,直到将结果枚举到列表中。

首先结果肯定是空的因为你用不同的值过滤了相同的属性,假设你纠正了这个,回答了你的问题,对于iqueryable它只会调用数据库一次,对于Enumerable对象也是一样的,因为迭代只会发生当你用foreach或调用GetEnumerator迭代对象时,在你的情况下,你没有这样做

如果您有许多过滤器,由于许多委托创建和调用以及对Where的许多调用,开销可能会变得明显。也就是说,您可能应该运行性能测试来确定它是否真的是一个问题。

您可以重构代码以测试相同谓词中的所有过滤器:

public List<MyObject> FilteringFunction(List<MyObject> listToFilter, List<Filter> filters)
{
    Func<MyObject, bool> predicate =
        x =>
            filters.All(f => x.Value.Contains(f.Value));
    return listToFilter.Where(predicate).ToList();
}

PS:关于这个:

// A dirty way to have an enumerable instead of List

有一个更干净的方法:

var enumerableToFilter = listToFilter as IEnumerable<MyObject>;

甚至:

var enumerableToFilter = listToFilter.AsEnumerable();