C#,使用多个 .Where() 语句过滤 IEnumerable,性能命中
C#, filtering IEnumerable with multiple .Where() statements, performance hits
我有一个 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();
}
我是否要只遍历我的 collection 一次? (因为我只有一个使用 LINQ 的数据库调用 SQL)
如果您有很多过滤器,开销可能会变得很明显,因为有很多委托创建和调用以及对 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();
Enumerables 推迟执行,直到你迭代它们,并且多个过滤器被应用到通过集合的单个迭代中。与其他 linq 语句结合可能会强制提前枚举,我没有测试每一种组合。这只会是非常大的数据集或低规格性能关键系统的问题。
这是一个使用 Visual Studios 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 都通过了第一个过滤器,因此应用了两个过滤器。
要点: 请注意,直到将可枚举项读取到列表中,过滤才真正发生。在将结果枚举到列表之前,您将能够继续附加过滤器。
首先,结果肯定是空的,因为你用不同的值过滤了相同的属性,假设你更正了这个,回答了你的问题,对于可查询的,它会调用数据库一次,对于 Enumerable 对象是相同的,因为迭代将在您使用 foreach 迭代对象或调用 GetEnumerator 时发生,在您的情况下您没有这样做
我有一个 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();
}
我是否要只遍历我的 collection 一次? (因为我只有一个使用 LINQ 的数据库调用 SQL)
如果您有很多过滤器,开销可能会变得很明显,因为有很多委托创建和调用以及对 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();
Enumerables 推迟执行,直到你迭代它们,并且多个过滤器被应用到通过集合的单个迭代中。与其他 linq 语句结合可能会强制提前枚举,我没有测试每一种组合。这只会是非常大的数据集或低规格性能关键系统的问题。
这是一个使用 Visual Studios 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 都通过了第一个过滤器,因此应用了两个过滤器。
要点: 请注意,直到将可枚举项读取到列表中,过滤才真正发生。在将结果枚举到列表之前,您将能够继续附加过滤器。
首先,结果肯定是空的,因为你用不同的值过滤了相同的属性,假设你更正了这个,回答了你的问题,对于可查询的,它会调用数据库一次,对于 Enumerable 对象是相同的,因为迭代将在您使用 foreach 迭代对象或调用 GetEnumerator 时发生,在您的情况下您没有这样做