.NET LINQ中的表达式树Expression原理简单刨析

在使用LINQ的使用过程中,经常会用到Lambda表达式,Lambda表达本质是委托,我们在使用过程中如果参数需要拼接,那么我们就需要用到表达式Expression,他的基本使用如下.

Expression<Func<int, int>> ExTree = s => s>2+s;

从中可以看出,这个使用不过嵌套了委托,真的是这样吗?下面我们就表达式树做简单介绍。

一、什么是表达式树(Expression)

    它是LINQ引入了一种名为Expression的新类型,.NET编译器将分配给Expression <TDelegate>的lambda表达式转换为Expression树,代码不可执行。LINQ查询提供程序使用此表达式树作为数据结构,可以构建运行时查询。我们跟委托对比一下

Func<int, int> func = s =>  2 + s*3+1;//委托 Console.WriteLine(func(3));Expression<Func<int, int>> ExTree = s => 2 + s*3+1;//表达式树  Func<int, int> rel = ExTree.Compile();//转换成委托  Console.WriteLine(rel(3));  //结果都是12,没有差别

就上面的基本使用我们用反编译工具IL查看一下,使用IL得到表达式树。如下:

  //这个是lambda  Func<int, int> func = (int s) => 2 + s*3+1;  Console.WriteLine(func(3));  //表达式树  ParameterExpression parameterExpression = Expression.Parameter(typeof(int), "s");  Expression<Func<int, int>> ExTree = Expression.Lambda<Func<int, int>>(Expression.Add(Expression.Constant(2, typeof(int)), parameterExpression), new ParameterExpression[1]  {    parameterExpression  });  Func<int, int> rel = ExTree.Compile();  Console.WriteLine(rel(3));  //输出结果都是:12

从上可以看出,Expression树不是简单的对委托的封装,它是有复杂的实现逻辑。那么表达式树是啥?其实它就是微软工程师设计的一种语法糖。

我们可以把上面反编译的代码拿到vs里翻译成自己的代码运行试一试。

//翻译后二代码ParameterExpression parameterExpression = Expression.Parameter(typeof(int), "s");//声明 ConstantExpression constant3 = Expression.Constant(3, typeof(int));//3 BinaryExpression binary = Expression.Multiply(parameterExpression, constant3);//s*3 ConstantExpression constant2 = Expression.Constant(2, typeof(int));//2 BinaryExpression binary2 = Expression.Add(constant2, binary);//2+s*3 ConstantExpression constant1 = Expression.Constant(1, typeof(int));//1 BinaryExpression binary3= Expression.Add(constant1, binary2);//2 + s * 3 + 1 //把表达式引入,并且表达式树有一个参数 Expression<Func<int, int>> ExTree2 = Expression.Lambda<Func<int, int>>(binary3, new ParameterExpression[1] {      parameterExpression }); Func<int, int> rel2 = ExTree2.Compile(); Console.WriteLine(rel2(3));//结果跟上面一样:12

从上面代码的执行过程可以看出,执行过程是如下图:

.NET LINQ中的表达式树Expression原理简单刨析

从图形中可以看出,这像什么?这不是二叉树么,小编查阅资料,执行过程确实是二叉树,它的本质就是个二叉树,是一个二叉树的数据结构。

    Expression树可以通过Compile转换成委托从而变成可执行,但是表达式树不支持方法体,只支持一句话的语法。

    当然在实际运用中不能这么用,主要还是作为对象查询条件使用,最终转换成可执行的SQL语句,注意必须是linq to SQL类型,如下:

 Expression<Func<Customer, bool>> whe = w => w.age > 30 && w.name.Contains("刘"); var rel5 = GetCustomer().AsQueryable().Where(whe);//必须是Queryable类型

这样就,方便查询条件参数化,大家也可以自己封装一下,形成自己的查询类。

二、表达式树跟委托的区别

    从上可以看出委托跟表达式树有本质的区别,委托本质是个类(可以从反编译看出),里面的元素是密封的,外面获取不了运行时,委托是为了实现方法的计算。表达式树是另一个类,只不过Expression 的泛型类型是委托,它可以通过compile可以得到委托,从而实现计算。表达式树的本质上是二叉树,是一个二叉树的数据结构。

    表达式目录树可以通过lambda快速声明,最终的目标是实现SQL的转换,通过lambda形式快捷声明Expression,然后通过linq解析Expression 翻译成sql,完成对操作数据库语句的封装。而委托不具备这个功能。

结语

    本文介绍了表达式树的简单原理,并对比了与委托的区别,以及用途。表达式树最终是实现SQL的转换,如何实现SQL的转换,大家可以想想,或者查看linq开源代码。

    写作水平有限,希望本文对大家学习和工作有一定参考价值,谢谢大家的支持。

下面附图IL配置后才能显示表达式树的反编译代码,如下

.NET LINQ中的表达式树Expression原理简单刨析