假设我有一个List<T>
,其中包含1000个项目。
然后我将其传递给过滤此List的方法。
当它落到各种情况时(例如可能有50个),List<T>
可能会对其执行多达50个不同的Linq Where()
操作。
我对此运行感兴趣尽快。因此,我不希望每次在List<T>
上执行此Where()
过滤。
基本上我需要它来推迟List<T>
的实际操作,直到所有过滤器都被应用为止。
这是由编译器本地完成的吗?或者只是当我在IEnumerable上调用.ToList()时List<T>.Where()
返回,或者我应该对X执行Where()
操作(其中X = List.AsQueryable())?
希望这是有道理的。
答案 0 :(得分:4)
是,原生支持延迟执行。每次在List上应用查询或lambda表达式时,查询都会存储仅在查询时调用.ToList()时执行的所有表达式。
答案 1 :(得分:3)
每次调用Where
都会创建一个新对象,该对象知道您的过滤器及其被调用的序列。
当这个新对象被要求输入一个值时(我在迭代器和迭代器之间故意模糊),它将询问原始序列的下一个值,检查过滤器,并返回值或迭代返回,询问原始序列的下一个值等。
因此,如果您拨打Where
50次(如list.Where(...).Where(...).Where(...)
中所示,您最终需要在调用堆栈中上下调整每个项目至少50次。表现多少会有什么影响?我不知道:你应该测量它。
一种可能的替代方法是构建表达式树,然后在最后将其编译成委托,然后 调用Where
。这肯定会有点费力,但可能最终会更有效率。实际上,它可以让你改变这个:
list.Where(x => x.SomeValue == 1)
.Where(x => x.SomethingElse != null)
.Where(x => x.FinalCondition)
.ToList()
到
list.Where(x => x.SomeValue == 1 && x.SomethingElse != null && x.FinalCondition)
.ToList()
如果你知道你只是将很多“where”过滤器组合在一起,这可能最终比通过IQueryable<T>
更有效率。与以往一样,在做更复杂的事情之前,请检查最简单的解决方案的性能。
答案 2 :(得分:0)
问题和评论中有很多失败。答案很好但是没有足够的努力来突破失败。
假设您有一个列表和一个查询。
List<T> source = new List<T>(){ /*10 items*/ };
IEnumerable<T> query = source.Where(filter1);
query = query.Where(filter2);
query = query.Where(filter3);
...
query = query.Where(filter10);
[惰性评估]是否由编译器本地完成?
没有。懒惰评估是由于Enumerable.Where
的实施此方法通过使用延迟执行来实现。立即返回值是一个对象,它存储执行操作所需的所有信息。在通过直接调用其GetEnumerator方法或在Visual C#中使用foreach或在Visual Basic中使用For Each来枚举对象之前,不会执行此方法表示的查询。
调用List.AsQueryable()。ToList()的速度惩罚
请勿致电AsQueryable
,您只需使用Enumerable.Where
。
因此不会阻止50个调用深度调用堆栈
深度调用堆栈远不如首先使用高效过滤器重要。如果您可以尽早减少元素数量,则可以在以后减少方法调用次数。