一、前言
一直想写一篇Dpper的定制化扩展的文章,但是里面会设计到对Lambda表达式的解析,而解析Lambda表达式,就必须要知道表达式树的相关知识点。我希望能通过对各个模块的知识点或者运用能够多一点的讲解,能够帮助到园友了解得更多。虽然讲解得不全面,如果能成为打开这块的一把钥匙,也是蜗牛比较欣慰的。
表达式系列目录
C# 表达式树讲解(一)
二、表达树理解
表达式树以树形数据结构表示代码,其中每一个节点都是一种表达式,它将我们原来可以直接由代码编写的逻辑以表达式的方式存储在树状的结构里,从而可以在运行时去解析这个树,然后执行,实现动态的编辑和执行代码。在.Net 里面的Linq to SQL就是对表达式树的解析。
这里先讲解下表达式和表达式树,表达式相信大家都知道,比如x+5或者5,都可以算是表达式,而表达式树里面的树指的二叉树,也就是表达式的集合,C#中的Expression类就是表达式类。对于一棵表达式树,其叶子节点都是参数或者常数,非叶子节点都是运算符或者控制符。
2.1、表达式的创建
Lambda表达式方法:
Expression<Func<int, int,bool>> fun = (x, y) => x < y
这种方法创建出的表达式根节点类型为ExpressionType.Lambda,Type类型为返回值类型typeof(bool)
组装法(通过 API 创建表达式树):
ParameterExpression numParam = Expression.Parameter(typeof(int), "num"); ConstantExpression five = Expression.Constant(5, typeof(int)); BinaryExpression numLessThanFive = Expression.LessThan(numParam, five); Expression<Func<int, bool>> lambda1 = Expression.Lambda<Func<int, bool>>( numLessThanFive, new ParameterExpression[] { numParam });
我们先创建了两个参数表达式num和5,然后用LessThan组装在一起,最终的表达式为“num<5”,expr的节点类型为LessThan,Type类型为typeof(bool)
我们先看看表达式树里面的构造
首先Expression<TDelegate>的功能是将强类型Lambda表达式表示为表达式树形式的数据结构,他的父类是LambdaExpression,比较他们代码可知,Lambda表达式的主体,名称和参数全部保存在LambdaExpression里面。
Expression<TDelegate>与LambdaExpression代码截图:
LambdaExpression里面的Body就是我们的表达式。
C#表达式给我们提供了丰富的表达式类,进入到LambdaExpression类里面
方法返回类型以“Expression”结尾的,基本上都是一个表达式类。
每个表达式代表的定义和创建方法,可以参照微软官方文档https://docs.microsoft.com/zh-cn/dotnet/api/system.linq.expressions.binaryexpression?view=netframework-4.8
下面是平常使用最多的表达式
ConstantExpression:常量表达式
ParameterExpression:参数表达式
UnaryExpression:一元运算符表达式
BinaryExpression:二元运算符表达式
TypeBinaryExpression:is运算符表达式
ConditionalExpression:条件表达式
MemberExpression:访问字段或属性表达式
MethodCallExpression:调用成员函数表达式
Expression<TDelegate>:委托表达式
2.2、表达式的解析
表达式树解析
通过LambdaExpression类我们可以知道,表达式树包含:参数[Parameters],表达式树类型[NodeType],表达式[Body],返回类型[ReturnType],Lambda表达式的委托[Compile]以及Lambda表达式名称[name],如图所示:
表达式解析:
所有的表达式都包含:左节点【Left】,右节点【Right】,类型【NodeType】,不同的表达式还会有其他属性,这里的左右节点依旧是表达式。
下图是BinaryExpression表达式截图
表达式树和表达式里面的类型NodeType是一个枚举,一共有85个类型,有兴趣的朋友可以去了解下。
常用的类型如下:
ExpressionType.And:C#中类似于&
ExpressionType.AndAlso:C#中类似于&&
ExpressionType.Or:C#中类似于|
ExpressionType.OrElse:C#中类似于||
ExpressionType.Equal:C#中类似于==
ExpressionType.NotEqual:C#中类似于!=
ExpressionType.GreaterThan:C#中类似于>
ExpressionType.GreaterThanOrEqual:C#中类似于>=
ExpressionType.LessThan:C#中类似于<
ExpressionType.LessThanOrEqual:C#中类似于<=
ExpressionType.Add:C#中类似于+
ExpressionType.AddChecked:C#中类似于+
ExpressionType.Subtract:C#中类似于-
ExpressionType.SubtractChecked:C#中类似于-
ExpressionType.Divide:C#中类似于/
ExpressionType.Multiply:C#中类似于*
ExpressionType.MultiplyChecked:C#中类似于*
2.3、编译表达式树
在表达式创建那,我们组合创建了一个Lambda表达式,那么应该怎么使用它呢?在“表达式的解析”里面,LambdaExpression类和Expression<TDelegate>类都有一个Compile的方法,学名是Lambda表达式的委托,其实就是Lambda表达式编译函数的委托,所以我们只需要调用他,得到的结果就是一个函数方法。
代码修改如下:
ParameterExpression numParam = Expression.Parameter(typeof(int), "num"); ConstantExpression five = Expression.Constant(5, typeof(int)); BinaryExpression numLessThanFive = Expression.LessThan(numParam, five); Expression<Func<int, bool>> lambda1 = Expression.Lambda<Func<int, bool>>( numLessThanFive, new ParameterExpression[] { numParam }); Console.WriteLine($"Lambda的内容:{lambda1.ToString()}"); //表达式的编译 var func = lambda1.Compile(); Console.WriteLine($"Lambda的运行结果:{func(6)}");
运行结果
三、总结
这里我们对表达式做了基本的讲解,相信大家对Lambda表达式有了初步的了解,下面我们将继续讲解对一个表达式树的遍历。
出处:https://www.cnblogs.com/snailblog/p/11521043.html
=======================================================================================
C# 表达式树遍历(二)
一、前言
上一篇我们对表达式树有了初步的认识,这里我们将对表达式树进行遍历,只有弄清楚了他的运行原理,我们才可以对他进行定制化修改。
表达式系列目录
C# 表达式树遍历(二)
二、表达式树的遍历
要查看表达式树的遍历,肯定不能直接用.Net Framework封装的方法,因为.Net Framework框架是闭源的,除了看中间语言(IL)去查看。我们就用ExpressionVisitor类查看一下他的运行原理,看了下ExpressionVisitor类,里面都是对各个表达式的访问,而且都是虚拟函数,我们可以对他进行override。
ExpressionVisitor类里面都是对各个类型的表达式进行访问,为了更好的理解里面的访问顺序,蜗牛把里面的虚函数都override了一遍,然后跟踪里面的执行顺序。【傻眼了,35个虚函数需要override,内心是很拒绝的,vs2019有没有重写父类虚函数的快捷键啊!!!!!!!】
ExpressionVisitor类相关介绍:https://docs.microsoft.com/zh-cn/dotnet/api/system.linq.expressions.expressionvisitor?view=netframework-4.8
2.1、ExpressionVisitor类的跟踪
为了不改变ExpressionVisitor类原来的访问,创建的SnailExpressionVisitor.cs 文件只在重写方法里面添加日志打印。
代码如下:
public class SnailExpressionVisitor : ExpressionVisitor { public override Expression Visit(Expression node) { Console.WriteLine($"访问了 Visit,内容:{node.ToString()}"); return base.Visit(node); } protected override CatchBlock VisitCatchBlock(CatchBlock node) { Console.WriteLine($"访问了 VisitCatchBlock,内容:{node.ToString()}"); return base.VisitCatchBlock(node); } protected override ElementInit VisitElementInit(ElementInit node) { Console.WriteLine($"访问了 VisitElementInit,内容:{node.ToString()}"); return base.VisitElementInit(node); } protected override LabelTarget VisitLabelTarget(LabelTarget node) { Console.WriteLine($"访问了 VisitLabelTarget,内容:{node.ToString()}"); return base.VisitLabelTarget(node); } protected override MemberAssignment VisitMemberAssignment(MemberAssignment node) { Console.WriteLine($"访问了 VisitMemberAssignment,内容:{node.ToString()}"); return base.VisitMemberAssignment(node); } protected override MemberBinding VisitMemberBinding(MemberBinding node) { Console.WriteLine($"访问了 VisitMemberBinding,内容:{node.ToString()}"); return base.VisitMemberBinding(node); } protected override MemberListBinding VisitMemberListBinding(MemberListBinding node) { Console.WriteLine($"访问了 VisitMemberListBinding,内容:{node.ToString()}"); return base.VisitMemberListBinding(node); } protected override MemberMemberBinding VisitMemberMemberBinding(MemberMemberBinding node) { Console.WriteLine($"访问了 VisitMemberMemberBinding,内容:{node.ToString()}"); return base.VisitMemberMemberBinding(node); } protected override SwitchCase VisitSwitchCase(SwitchCase node) { Console.WriteLine($"访问了 VisitSwitchCase,内容:{node.ToString()}"); return base.VisitSwitchCase(node); } protected override Expression VisitBinary(BinaryExpression node) { Console.WriteLine($"访问了 VisitBinary,内容:{node.ToString()}"); return base.VisitBinary(node); } protected override Expression VisitBlock(BlockExpression node) { Console.WriteLine($"访问了 VisitBlock,内容:{node.ToString()}"); return base.VisitBlock(node); } protected override Expression VisitConditional(ConditionalExpression node) { Console.WriteLine($"访问了 VisitConditional,内容:{node.ToString()}"); return base.VisitConditional(node); } protected override Expression VisitConstant(ConstantExpression node) { Console.WriteLine($"访问了 VisitConstant,内容:{node.ToString()}"); return base.VisitConstant(node); } protected override Expression VisitDebugInfo(DebugInfoExpression node) { Console.WriteLine($"访问了 VisitDebugInfo,内容:{node.ToString()}"); return base.VisitDebugInfo(node); } protected override Expression VisitDefault(DefaultExpression node) { Console.WriteLine($"访问了 VisitDefault,内容:{node.ToString()}"); return base.VisitDefault(node); } protected override Expression VisitDynamic(DynamicExpression node) { Console.WriteLine($"访问了 VisitDynamic,内容:{node.ToString()}"); return base.VisitDynamic(node); } protected override Expression VisitExtension(Expression node) { Console.WriteLine($"访问了 VisitExtension,内容:{node.ToString()}"); return base.VisitExtension(node); } protected override Expression VisitGoto(GotoExpression node) { Console.WriteLine($"访问了 VisitGoto,内容:{node.ToString()}"); return base.VisitGoto(node); } protected override Expression VisitIndex(IndexExpression node) { Console.WriteLine($"访问了 VisitIndex,内容:{node.ToString()}"); return base.VisitIndex(node); } protected override Expression VisitInvocation(InvocationExpression node) { Console.WriteLine($"访问了 VisitInvocation,内容:{node.ToString()}"); return base.VisitInvocation(node); } protected override Expression VisitLabel(LabelExpression node) { Console.WriteLine($"访问了 VisitLabel,内容:{node.ToString()}"); return base.VisitLabel(node); } protected override Expression VisitLambda<T>(Expression<T> node) { Console.WriteLine($"访问了 VisitLambda,内容:{node.ToString()}"); return base.VisitLambda(node); } protected override Expression VisitListInit(ListInitExpression node) { Console.WriteLine($"访问了 VisitListInit,内容:{node.ToString()}"); return base.VisitListInit(node); } protected override Expression VisitLoop(LoopExpression node) { Console.WriteLine($"访问了 VisitLoop,内容:{node.ToString()}"); return base.VisitLoop(node); } protected override Expression VisitMember(MemberExpression node) { Console.WriteLine($"访问了 VisitMember,内容:{node.ToString()}"); return base.VisitMember(node); } protected override Expression VisitMemberInit(MemberInitExpression node) { Console.WriteLine($"访问了 VisitMemberInit,内容:{node.ToString()}"); return base.VisitMemberInit(node); } protected override Expression VisitMethodCall(MethodCallExpression node) { Console.WriteLine($"访问了 VisitMethodCall,内容:{node.ToString()}"); return base.VisitMethodCall(node); } protected override Expression VisitNew(NewExpression node) { Console.WriteLine($"访问了 VisitNew,内容:{node.ToString()}"); return base.VisitNew(node); } protected override Expression VisitNewArray(NewArrayExpression node) { Console.WriteLine($"访问了 VisitNewArray,内容:{node.ToString()}"); return base.VisitNewArray(node); } protected override Expression VisitParameter(ParameterExpression node) { Console.WriteLine($"访问了 VisitParameter,内容:{node.ToString()}"); return base.VisitParameter(node); } protected override Expression VisitRuntimeVariables(RuntimeVariablesExpression node) { Console.WriteLine($"访问了 VisitRuntimeVariables,内容:{node.ToString()}"); return base.VisitRuntimeVariables(node); } protected override Expression VisitSwitch(SwitchExpression node) { Console.WriteLine($"访问了 VisitSwitch,内容:{node.ToString()}"); return base.VisitSwitch(node); } protected override Expression VisitTry(TryExpression node) { Console.WriteLine($"访问了 VisitTry,内容:{node.ToString()}"); return base.VisitTry(node); } protected override Expression VisitTypeBinary(TypeBinaryExpression node) { Console.WriteLine($"访问了 VisitTypeBinary,内容:{node.ToString()}"); return base.VisitTypeBinary(node); } protected override Expression VisitUnary(UnaryExpression node) { Console.WriteLine($"访问了 VisitUnary,内容:{node.ToString()}"); return base.VisitUnary(node); } }
调用方法:
Expression<Func<int, int, bool>> fun = (x, y) => x - y > 5; var treeModifier = new SnailExpressionVisitor(); Expression modifiedExpr = treeModifier.Visit(fun);
运行结果:
从打印的日志里面可以看出,
1、每次访问表达式类时,都会先去调用Visit函数,估计他是在Visit里面判定表达式类,然后在根据表达式类的类型,调用访问改表达式的函数
2、对Lambda表达式类,是先访问的是Expression<T>。Expression<T>是不是很熟悉,上一章说过他的作用是将强类型Lambda表达式表示为表达式树形式的数据结构,解析成功之后才对表达式的访问
3、对于表达式先解析的是左边,左边的内容解析完了之后在解析右边,如(x-y)>5,解析的顺序是:x-y=>x=>y=>5
2.2、修改表达式树
既然我们弄清楚了表达式树的访问,现在我们就可以对他进行编辑修改了。
上面我们判断的是x-y>5,现在我们规定,将“-”改成“+”,“>”改成“>=”
对VisitBinary方法修改代码如下:
protected override Expression VisitBinary(BinaryExpression node) { Console.WriteLine($"访问了 VisitBinary,内容:{node.ToString()}"); if (node.NodeType == ExpressionType.GreaterThan) { Expression left = this.Visit(node.Left); Expression right = this.Visit(node.Right); var result = Expression.MakeBinary(ExpressionType.GreaterThanOrEqual, left, right, node.IsLiftedToNull, node.Method); Console.WriteLine($"访问了 VisitBinary,更改之后的内容:{result.ToString()}"); return result; } else if (node.NodeType == ExpressionType.Subtract || node.NodeType == ExpressionType.SubtractChecked) { Expression left = this.Visit(node.Left); Expression right = this.Visit(node.Right); var result = Expression.MakeBinary(ExpressionType.Add, left, right, node.IsLiftedToNull, node.Method); Console.WriteLine($"访问了 VisitBinary,更改之后的内容:{result.ToString()}"); return result; } else { return base.VisitBinary(node); } }
调用方法:
Expression<Func<int, int, bool>> fun = (x, y) => x - y > 5; var treeModifier = new SnailExpressionVisitor(); Expression modifiedExpr = treeModifier.Visit(fun); Console.WriteLine($"Lambda的转换最后结果:{modifiedExpr.ToString()}");
运行结果如下
三、总结
对表达树的讲解已经完成了,但是说了这么久,对真实的开发有什么作用呢?后面我将利用Lambda表达式写一个对现有数据分页的公共方法,同时在对Dapper的扩展也会用到相关知识点,大家拭目以待吧……