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
从上面代码的执行过程可以看出,执行过程是如下图:
从图形中可以看出,这像什么?这不是二叉树么,小编查阅资料,执行过程确实是二叉树,它的本质就是个二叉树,是一个二叉树的数据结构。
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配置后才能显示表达式树的反编译代码,如下
本文来自博客园,作者:{春光牛牛,yak},转载请注明原文链接:https://www.cnblogs.com/yakniu/p/16494022.html
欢迎各位大佬们评论指正
QQ讨论群:610129902