1

假设我有一个List<T>包含 1000 个项目的项目。

然后我将其传递给过滤此列表的方法。当它通过各种情况(例如可能有 50 种情况)时,List<T>可能有多达 50 种不同的 LinqWhere()操作对其执行。

我对尽可能快的运行很感兴趣。因此,我不希望List<T>每次Where()对其执行 a 时都对其进行过滤。

本质上,我需要它来推迟对 的实际操作,List<T>直到应用了所有过滤器。

这是由编译器本地完成的吗?或者只是当我在返回的 IEnumerable 上调用 .ToList() 时List<T>.Where(),还是应该Where()对 X 执行操作(其中 X = List.AsQueryable())?

希望这是有道理的。

4

3 回答 3

4

的,本机支持延迟执行。每次在 List 上应用查询或 lambda 表达式时,查询都会存储仅当您在查询上调用 .ToList() 时执行的所有表达式。

于 2010-06-17T10:27:10.860 回答
3

每次调用Where都会创建一个新对象,该对象知道您的过滤器及其被调用的顺序。

当这个新对象被询问一个值时(我故意在迭代器和可迭代之间模糊)它会询问原始序列的下一个值,检查过滤器,然后返回值或迭代回来,询问下一个值的原始序列等。

因此,如果您调用50Where次(如在list.Where(...).Where(...).Where(...)测量它。

一种可能的替代方法是构建一个表达式树,然后在最后将其编译成一个委托,然后调用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”过滤器组合在一起,那么这可能最终会比通过 via 更有效IQueryable<T>。与以往一样,在做更复杂的事情之前检查最简单的解决方案的性能。

于 2010-06-17T10:53:28.867 回答
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 个调用深度调用堆栈

调用堆栈的深度远不如首先拥有一个高效的过滤器重要。如果您可以尽早减少元素的数量,那么您以后可以减少方法调用的数量。

于 2010-06-17T14:23:46.503 回答