小试 Linq Expression
0X00 背景
表达式是当今编程语言中最重要的组成成分。简单的说,表达式就是变量、数值、运算符、函数组合起来,表示一定意义的式子。例如下面这些都是(C#)的表达式:
3 //常数表达式
a //变量或参数表达式
!a //一元逻辑非表达式
a + b //二元加法表达式
Math.Sin(a) //方法调用表达式
new StringBuilder() //new 表达式
Expression Tree是.NET 3.5引入的,System.Linq.Expression命名空间下的Expression类和它的诸多子类就是这一数据结构的实现。每个表达式都可以表示成Expression某个子类的实例。每个Expression子类都按照相应表达式的特点储存自己的子节点。例如BinaryExpression就表示各种二元运算符的表达式。它的Left和Right属性就是参与二元运算的两个运算数。
0X01 目录树的构成
Expression Tree是.NET 3.5引入的,究竟什么是表达式目录树(Expression Tree),它是一种抽象语法树或者说它是一种数据结构,通过解析表达式目录树,我们首先来看看如何构造出一个表达式目录树,最简单的方法莫过于使用Lambda表达式,ConstantExpression是一个常量表达式,BinaryExpression是一个二元表达式,它们都继承于抽象的基类Expression,而泛型的Expression
BinaryExpression body = Expression.Add(Expression.Constant(2),Expression.Constant(3));
Expression> expression = Expression.Lambda>(body, null);
Func lambda = expression.Compile();
Console.WriteLine(lambda());
Add表示计算和,运行结果如下
现在我们知道了表达式目录树的组成,再来看看.NET Framework到底提供了哪些表达式,请看下图
0X02 编译方法Compile
这里我们仍要使用诸多表达式中与众不同的LambdaExpression。LambdaExpression.Compile是将Lambda表达式的表达式树真的编译成一个.NET方法。我们看看这段代码:
ParameterExpression pi = Expression.Parameter(typeof(int), "i");
LambdaExpression fexp =
Expression.Lambda(
Expression.Add(pi, Expression.Constant(1))
, pi);
Delegate lambda3 = fexp.Compile();
Console.WriteLine(lambda3.DynamicInvoke(3));
这个表达式树其实就是 i => i + 1。后面的Compile方法生成了一个Delegate。如果我们试着获取这个Delegate的类型会发现他是Func
Expression.Lambda这个方法还有一个泛型版重载,它可以创建如同C#自己生成一样的强类型LambdaExpression。强类型LambdaExpression的Compile就更好了,能直接生成强类型的委托。改成如下代码
ParameterExpression pi = Expression.Parameter(typeof(int), "i");
var fexp =
Expression.Lambda>(
Expression.Add(pi, Expression.Constant(1))
, pi);
var f = fexp.Compile();
Console.WriteLine(f(3));
0X03 执行表达式目录树
前面已经可以构造出一个表达式目录树了,现在看看如何去执行表达式目录树。我们需要调用Compile方法来创建一个可执行委托,并且调用该委托,这里我们只要简单的调用Compile方法就可以了,事实上在.NET Framework中是调用了一个名为ExpressionCompiler的内部类来做表达式目录树的执行(注意此处的Compiler不等同于编译器的编译)运行结果如图
ParameterExpression paraLeft = Expression.Parameter(typeof(int), "a");
ParameterExpression paraRight = Expression.Parameter(typeof(int), "b");
BinaryExpression binaryLeft = Expression.Multiply(paraLeft, paraRight);
ConstantExpression conRight = Expression.Constant(2, typeof(int));
BinaryExpression binaryBody = Expression.Add(binaryLeft, conRight);
Expression> lambda2 = Expression.Lambda>(binaryBody, paraLeft, paraRight);
Func lambdaresult = lambda2.Compile();
int result = lambdaresult(2, 3);
Console.WriteLine(lambda2.ToString());
Console.WriteLine(result);
0X04 参考
https://www.cnblogs.com/Terrylee/archive/2008/08/01/custom-linq-provider-part-1-expression-tree.html
https://www.cnblogs.com/Ninputer/archive/2009/08/28/expression_tree1.html