在 C# 中编译 LINQ 查询有两个重要步骤。第一个是将 LINQ 查询语法转换为方法调用链,如 C# 语言规范的第 7.16 节所述。这个转换过程被详细说明,语言开发人员可以使用它在新的 CLR 语言上实现类似的查询语法。
第二步是将 lambda 表达式转换为表达式树,这发生在调用返回的查询方法时IQueryable
,而不是调用返回的方法时IEnumerable
。它是否曾经指定过这种转换是如何发生的,类似于查询语法转换过程的解释?
在 C# 中编译 LINQ 查询有两个重要步骤。第一个是将 LINQ 查询语法转换为方法调用链,如 C# 语言规范的第 7.16 节所述。这个转换过程被详细说明,语言开发人员可以使用它在新的 CLR 语言上实现类似的查询语法。
第二步是将 lambda 表达式转换为表达式树,这发生在调用返回的查询方法时IQueryable
,而不是调用返回的方法时IEnumerable
。它是否曾经指定过这种转换是如何发生的,类似于查询语法转换过程的解释?
实际上,表达式树的构造没有明确定义。编译器开发人员可以自由地使用他们想要的任何方法,当然前提是执行表达式会产生与调用 lambda 相同的结果。
这是来自 C# 语言规范的引用:
6.5.2 匿名函数转换为表达式树类型的评估
将匿名函数转换为表达式树类型会生成表达式树(第 4.6 节)。更准确地说,匿名函数转换的评估导致构建表示匿名函数本身结构的对象结构。表达式树的精确结构以及创建它的精确过程是由实现定义的。
我在最后添加了粗体字。
我怀疑这是故意未指定的,目的是让编译器开发人员可以自由地实施他们认为有帮助的任何优化。表达式树的严格规范会阻止这种情况。
它是否曾经指定过这种转换是如何发生的,类似于查询语法转换过程的解释?
正如 NSFW 所说,没有。
实际上,这些表达式树可以从一个框架到另一个框架变化。一个现实生活中的例子是这样的:
我们expression lambdas
用来通过表达式树获取属性信息。
如void DoSomething<P, K>(P model, Expression<Func<P, K> propertySelector)
, 和用法DoSomething(model, m => m.Property)
实际的财产查询是DoSomething
通过反射在内部完成的。这是非常经典的,并且此类代码的变体存在于互联网上。
现在,这很酷,它在 .NET 4.0 中运行良好。然而,一旦我尝试了 4.5,它就彻底崩溃了,因为底层表达式树发生了变化。
您可以肯定 Roslyn 将引入许多新的“错误”,因为某些客户端代码依赖于 lambdas 如何转换为表达式树的表示(如果您真的坚持这样做 - 使用Visitor
class 可以最大限度地减少破坏的机会)。
确保表达式树保持不变将是一项主要任务,而且它也会受到限制(例如,速度方面)