C# 表达式树遍历(二)
一、前言
我们对表达式树有了初步的认识,这里我们将对表达式树进行遍历,只有弄清楚了他的运行原理,我们才可以对他进行定制化修改。
表达式系列目录
c# 表达式树遍历(二)
二、表达式树的遍历
要查看表达式树的遍历,肯定不能直接用.net framework封装的方法,因为.net framework框架是闭源的,除了看中间语言(il)去查看。我们就用expressionvisitor类查看一下他的运行原理,看了下expressionvisitor类,里面都是对各个表达式的访问,而且都是虚拟函数,我们可以对他进行override。
expressionvisitor类里面都是对各个类型的表达式进行访问,为了更好的理解里面的访问顺序,蜗牛把里面的虚函数都override了一遍,然后跟踪里面的执行顺序。【傻眼了,35个虚函数需要override,内心是很拒绝的,vs2019有没有重写父类虚函数的快捷键啊!!!!!!!】
expressionvisitor类相关介绍:
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的扩展也会用到相关知识点,大家拭目以待吧……