【问题标题】:Does the compiler concatenate LINQ where queries?编译器是否连接 LINQ where 查询?
【发布时间】:2011-11-14 23:11:42
【问题描述】:

考虑以下两个相似的代码示例。

一个where 子句。

bool validFactory
  = fields
    .Where(
      fields => field.FieldType == typeof( DependencyPropertyFactory<T> ) &&
                field.IsStatic )
    .Any();

两个where 子句。

bool validFactory
  = fields
    .Where( field => field.FieldType == typeof( DependencyPropertyFactory<T> ) )
    .Where( field => field.IsStatic )
    .Any();

我更喜欢第二个,因为我发现它更具可读性,并且导致的格式问题更少,尤其是在使用自动格式时。将 cmets 放在单独的条件旁边(甚至上面)以阐明意图时也更清楚。

我的直觉说第二个代码示例效率会降低。我当然可以自己编写一个简单的测试(如果没有人知道答案,我也会这样做)。现在我认为这是完美的食物。 ;p

  1. 一个比另一个更有效吗?
  2. 编译器是否足够聪明,可以对此进行优化?

【问题讨论】:

标签: c# .net linq compilation


【解决方案1】:

不允许编译器对此进行优化,因为它不知道 Where() 的作用。例如,您可能使用记录其结果的版本重载了 Where()。 (抖动可以做优化,但实际上不太可能。)

效率差异不太可能显着。您可以分析您的应用程序以查看它是否重要。

更新:显然抖动确实在这里执行优化。请参阅 Eric Lippert 的回答。

【讨论】:

  • 抖动不做优化; LINQ-to-Objects 库中 Where 的实现执行优化;它知道它的参数何时是另一个 Where 或 Select 查询。
  • 抖动不是在这里进行优化的东西 - 它是 WHERE 语句背后的代码。返回的 IEnumerable 在技术上是惰性求值的,因此在您请求第一个元素时,两个 WHERE 子句都已创建,并且内部子句可以与外部子句惰性地组合。这不同于任何巧妙的抖动
  • 同样,如果您使用 C#4.0 的 'select'、'where' 和 'from' 关键字(在内部转换为这些调用),则允许编译器知道组合调用的语义,尽管我很确定 Visual Studio 目前没有执行此优化。
【解决方案2】:

我不希望这里有显着差异。但是,如果您强制枚举集合,那么我预计会有更多不同

bool validFactory   = fields
    .Where( field => field.FieldType == typeof( DependencyPropertyFactory<T> ) )
    .ToList()
    .Where( field => field.IsStatic )     
    .ToList()
    .Any(); 

在您的两个原始代码示例中,我看到相同的执行 - 第一项检查 FieldType,然后检查 IsStatic,如果存在则返回 true。否则检查第二项,依此类推。整个集合不需要解析。

在上面的示例中,整个集合将被解析为 FieldType,独立于 IsStatic 检查。这可能效率较低。请注意,这在您的任何一个 sn-ps 中都不是必需的。

【讨论】:

    【解决方案3】:

    编译器不会尝试优化连续的“where”调用。 运行时库会这样做。如果您有一大堆“where”和“select”调用并排在一起,运行时将尝试将它们重新组织成更有效的形式。

    在一些不寻常的情况下,“优化”当然会使事情变得更糟。我似乎记得 Jon Skeet 最近写了一篇关于这个的文章,虽然我不确定它在哪里。

    【讨论】:

    • 当您谈论 runtime 时,您是在谈论像 EF 或 L2S 这样的 ORM,还是 CLR?因为我希望它们会导致生成相同的 SQL 查询,但在 LINQ-to-Objects 中我希望不会发生任何优化。
    • 埃里克,我想你指的是这篇博文。 msmvps.com/blogs/jon_skeet/archive/2011/06/16/…
    • @Gabe:我说的是 LINQ-to-Objects。您对不会发生优化的期望与现实不符,因此我对您的建议是调整您的期望。
    • 我们只是在争论“运行时”这个词的定义吗?对我来说,“运行时”意味着 CLR,据我所知,它与它无关。当编译器选择Enumerable.WhereEnumerableIterator.Where 作为第二个Where 函数而不是Enumerable.Where 时,就会发生优化。我会说库或编译器(可能两者都有)负责优化而不是运行时。
    • @Gabe:对我来说,“运行时”是指任何在运行时工作的组件。在这种情况下,它是具有优化智能的库。
    猜你喜欢
    • 2012-04-14
    • 2011-02-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多