使用Linq </t>延迟执行List <t>

时间:2010-06-17 10:23:33

标签: linq list generics deferred-execution

假设我有一个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())?

希望这是有道理的。

3 个答案:

答案 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个调用深度调用堆栈

深度调用堆栈远不如首先使用高效过滤器重要。如果您可以尽早减少元素数量,则可以在以后减少方法调用次数。