zoukankan      html  css  js  c++  java
  • C# 表达式树遍历(二)

    一、前言

    上一篇我们对表达式树有了初步的认识,这里我们将对表达式树进行遍历,只有弄清楚了他的运行原理,我们才可以对他进行定制化修改。

    表达式系列目录

    C# 表达式树讲解(一)

    C# 表达式树遍历(二)

     C# 表达式树分页扩展(三)

    C# 表达式树Lambda扩展(四)

    二、表达式树的遍历

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

    运行结果:

    image

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

    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()}");
    

    运行结果如下

    image

    三、总结

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

  • 相关阅读:
    Android 开发 深入理解Handler、Looper、Messagequeue 转载
    Android 开发 Handler的基本使用
    Java 学习 注解
    Android 开发 AlarmManager 定时器
    Android 开发 框架系列 百度语音合成
    Android 开发 框架系列 Google的ORM框架 Room
    Android 开发 VectorDrawable 矢量图 (三)矢量图动画
    Android 开发 VectorDrawable 矢量图 (二)了解矢量图属性与绘制
    Android 开发 VectorDrawable 矢量图 (一)了解Android矢量图与获取矢量图
    Android 开发 知晓各种id信息 获取线程ID、activityID、内核ID
  • 原文地址:https://www.cnblogs.com/snailblog/p/11521335.html
Copyright © 2011-2022 走看看