对于熟悉函数式编程语言的开发者而言,可能不需要再解释为什么说“表达式树(即 Expression Tree)是非常有用的”,但对其他人来讲,表达式树则是 C# 3.0 或 VB 9.0 的所有新特性中最引人注目的一个概念。
Charlie Calvert 讲解了表达式树的基础,他从语法描述开始,并以同 Visual Studio 2008 一起发行的示例程序——ExpressionTressVisualizer 为例,形象地展示了如何使用表达式树。
表达式树与 Lambda 表达式相关联,Lambda 表达式是可在行内实现预期代理和匿名代理的内容的一种方式。 Ian Griffiths 在他关于表达式树文章中,给出了一个非常不错的 Lambda 表达式介绍,以及 Lambda 与表达式树的关系。正如 Ian 所指出的,“Lambda 除了语法不同以外,并没有比我已掌握的匿名方法提供更多的东西,但是,它却使一些匿名方法无法实现的事情变成了可能。”
Func<int, bool> nonExprLambda = x => (x & 1) == 0;<br></br>Expression<Func<int, bool>> exprLambda = x => (x & 1) == 0;
[…]第二行更有趣,它变成 Func 的代理,将 Func 作为一种名为 Expression 的泛型的类型参数,然后按照相同的方式进行初始化,因此,你可能会认为两者是在完成同样的事情,但是,它却能让编译器知道了 Expression 类型,这就导致了不一样的行为;前者会直接将 Lambda 表达式编译为 IL,而后者则是通过对表达式进行评估后,生成一个可以创建并代表此表达式的对象树的 IL。
Charlie 仔细钻研了 Expression 的细节:
**Expression
** 类共有四个属性:
- Body:获取表达式的主体;
- Parameters:获取 Lambda 表达示的参数;
- NodeType:得到树中某些节点的表达式类型(ExpressionType),这是一个有 45 种不同值的枚举类型,代表表达式节点的所有可能类型,如返回常数、也可能返回参数、或者返回一个值是否小于另外一个(<),或者返回一个值是否大于另外一个(>),或者返回两个值的和(+)等等;
- Type:得到表达式的静态类型,在本例中,表达式的类型是Func<int, int, int>。
在 C# 3.0 和 VB 9.0 大部分新特性中,表达式树在“LINQ,尤其是 LINQ to SQL”中扮演了一个非常重要的角色:
var query = from c in db.Customers<br></br> where c.City == "Nantes"<br></br> select new { c.City, c.CompanyName };
LINQ 表达式会返回一个 IQueryable 类型的值,IQueryable 值包含有一个表达式树,由它来代表这个 LINQ 查询。当创建这个查询时,SQL 语句并没有被发送到数据库中,只有当你在代码中对这个 IQueryable 对象进行 Iterate 操作时,这个 SQL 语句才会被创建并执行,这个概念就是延迟执行。
Charlie Calvert 对这种方法的使用进行了说明:
因为查询是以一种高度抽象的数据结构封装后传送给编译器的,所以,编译器可以以它期望的任一方式自由地进行翻译。查询的执行次序和途径并非强制指定的,相反,它可以对表达树进行分析,以发现你所期望的结果,然后再决定如何处理;至少在理论上,它可以独立地考虑很多因素,诸如当前的网络流量、数据库的负载、它当前已有的可用结果集等等;虽然在 LINQ to SQL 中,它实际上并没有去考虑所有这些因素,但在理论上它可以考虑更多它关心的因素;此外,它还可以对表达式树进行分析,并将 LINQ to SQL 的输出结果转换成截然不同的形式,然后传递给你手写的自定义代码。
Marlon Grech 展示了如何应用表达式树,以及如何创建表达式解析器。Octavio Hernández 提供了一个表达式树的表达式类层次汇总表。
查看英文原文: Get a Grasp on Expression Trees
评论