一、前言

上一篇 <https://www.cnblogs.com/snailblog/p/11521043.html>
我们对表达式树有了初步的认识,这里我们将对表达式树进行遍历,只有弄清楚了他的运行原理,我们才可以对他进行定制化修改。

表达式系列目录

C# 表达式树讲解(一) <https://www.cnblogs.com/snailblog/p/11521043.html>

C# 表达式树遍历(二)

 C# 表达式树分页扩展(三) <https://www.cnblogs.com/snailblog/p/11521359.html>

C# 表达式树Lambda扩展(四) <https://www.cnblogs.com/snailblog/p/11525118.html>

二、表达式树的遍历

要查看表达式树的遍历,肯定不能直接用.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

<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);
运行结果:


<https://img2018.cnblogs.com/blog/1764554/201909/1764554-20190915102305051-1533802902.png>

从打印的日志里面可以看出,

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()}");
运行结果如下


<https://img2018.cnblogs.com/blog/1764554/201909/1764554-20190915102306070-1298442000.png>

三、总结


对表达树的讲解已经完成了,但是说了这么久,对真实的开发有什么作用呢?后面我将利用Lambda表达式写一个对现有数据分页的公共方法,同时在对Dapper的扩展也会用到相关知识点,大家拭目以待吧……

友情链接
ioDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:ixiaoyang8@qq.com
QQ群:637538335
关注微信