如果不先编译表达式树,就无法执行它,因为表达式树有不同的用途:它们作为可执行代码的灵活蓝图,而不是可执行代码本身(相当不灵活)。
表达式树是在高于代码本身的抽象级别描述代码(编译或未编译)的数据结构。与代码不同,数据结构可以被操纵以形成描述其他一些代码的不同结构。与数据结构不同,可以对代码进行评估以产生结果。
考虑这个例子:
Expression p1 = Expression.Parameter(typeof(int), "a");
Expression p2 = Expression.Parameter(typeof(int), "b");
Expression expr = Expression.Add(p1, p2);
它显示了一个expr
正在创建的表达式,描述了一个简单的表达式树a+b
。至此,我们有了一个树状数据结构,其中包含三个节点——两个节点代表参数,一个节点代表加法。我们可以用表达式树做很多事情,比如检查它的内容,找到它的返回类型等等。我们甚至可以操纵它的结构来基于它制作其他表达式:例如,我们可以添加第三个参数,然后 make a+b+c
,如下所示:
Expression p3 = Expression.Parameter(typeof(int), "c");
expr = Expression.Add(expr, p3);
然而,我们不能做的一件事是传递两个整数值并获得它们的总和:表达式树不提供评估它们的方法。为此,您需要真正的代码,而不是代码描述。
表达式树提供了一种通过编译将代码描述转换为代码的方法。将表达式树制作成 lambda 并Compile()
在其上调用方法后,您将返回编译后的CIL代码,如下所示:
ldarg.0
ldarg.1
add
ret
此代码可以传递两个值进行评估,并将总和返回给您。但是,一些信息在翻译过程中丢失了:具体来说,参数的名称a
并b
不再存在于编译的代码中:它们不是执行计算所必需的,因此它们已被删除。