C# 表达式树遍历(二)

一、前言

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

表达式系列目录

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的扩展也会用到相关知识点,大家拭目以待吧……

原文地址:https://www.cnblogs.com/snailblog/p/11521335.html

时间: 2024-07-30 22:50:16

C# 表达式树遍历(二)的相关文章

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

一.前言 前面我们知道了表达树的基本知识,也明白了怎么遍历和修改一个表达式,这里我们就一个实际的场景来进行功能开发. 表达式系列目录 C# 表达式树讲解(一) C# 表达式树遍历(二) 二.分页扩展 在实际的开发中,肯定会遇到这样的应用场景,一个数据源需要在页面上进行分页显示,并且页面上需要对该数据源有一个排名.本来分页可以在数据库层面完成,但是因为这里有一个排名功能,所谓的排名,就是需要查询出所有满足条件的数据,然后按照某个算法升序或者降序排列,然后按照进行排名.排名之后,然后根据分页信息,将

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

一.前言 本来计算这篇文章在后面需要运用的时候写的,但是既然写到表达式的扩展呢,就一起写完吧. 看到这个标题就有一种疑问,Lambda表达式本来就是表达式树,还需要怎么扩展?那就看看下面的内容,你就知道了. 表达式系列目录 C# 表达式树讲解(一) C# 表达式树遍历(二) C# 表达式树分页扩展(三) 二.Lambda扩展 这里先不忙解答上面的问题,我们先看下这样一个应用场景. 一个页面的请求,里面带有一些条件查询,请求类如下 public class ScoreRequest { publi

LinqToDB 源码分析——生成表达式树

当我们知道了Linq查询要用到的数据库信息之后.接下就是生成对应的表达式树.在前面的章节里面笔者就已经介绍过.生成表达式树是事实离不开IQueryable<T>接口.而处理表达式树离不开IQueryProvider接口.LinqToDB框架跟这俩个接口有关系的有三个类:Table<T>类.ExpressionQuery<T>类.ExpressionQueryImpl<T>类.其中最重要的是ExpressionQuery<T>类.他是Table&l

[.net 面向对象程序设计进阶] (5) Lamda表达式(二) 表达式树快速入门

[.net 面向对象程序设计进阶] (6) Lamda表达式(二) 表达式树快速入门 本节导读: 认识表达式树(Expression Tree),学习使用Lambda创建表达式树,解析表达式树. 学习表达式在程序设计中的优点:比如构造动态查询.动态构造表达式树完成未知对象属性访问,比反射的性能高出很多.我们可以说表达式树才是Lambda的精髓,是我们必须要熟练掌握并灵活运用的. 1.关于表达式树(Expression Tree) 表达式树以树形数据结构表示代码,其中每一个节点都是一种表达式,比如

07_2.二叉数,二叉树的简单应用:表达式树

""" 二叉树的list实现 """ def BinTree(data, left=None, right=None): """ 二叉树 :param data: 根结点的元素 :param left: 左子树元素 :param right: 右子树元素 :return: """ return [data, left, right] def is_empty_BinTree(btree)

[C#] 说说表达式树 - Expression Trees

说说表达式树 - Expression Trees 序 简单说下表达式树. 目录 介绍 表达式树以树形数据结构表示代码,其中每一个节点都是一种表达式,比如方法调用和 x < y 这样的二元运算等. 你可以对表达式树中的代码进行编辑和运算.这样能够动态修改可执行代码.在不同数据库中执行 LINQ 查询以及创建动态查询. 表达式树还能用于动态语言运行时 (DLR) 以提供动态语言和 .NET Framework 之间的互操作性,同时保证编译器编写员能够发射表达式树而非 Microsoft 中间语言

树(二)&mdash;&mdash;二叉树

目录 本章主要讲解内容为: 树的非递归遍历算法,两种版本 树的扩展前缀以及前缀中缀构建方法 基础知识 一.定义 二叉树的递归定义:二叉树是每个结点最多含有两棵子树的树结构. 二.性质 二叉树的递归定义标识着它具有很多递归性质. 二叉树的遍历.查找.构建.删除.复制和计数等全部可以用递归来实现,详见代码. 三.构建 二叉树的构建方法有:硬编码生成.扩展前缀.前缀结合中缀等.我实现了后两种方法. 四.遍历 二叉树的递归遍历非常简单,参见代码. 主要分析二叉树的非递归遍历,除了书上的版本外,我自己另写

[C#] C# 知识回顾 - 表达式树 Expression Trees

C# 知识回顾 - 表达式树 Expression Trees 目录 简介 Lambda 表达式创建表达式树 API 创建表达式树 解析表达式树 表达式树的永久性 编译表达式树 执行表达式树 修改表达式树 调试 简介 表达式树以树形数据结构表示代码,其中每一个节点都是一种表达式,比如方法调用和 x < y 这样的二元运算等. 你可以对表达式树中的代码进行编辑和运算.这样能够动态修改可执行代码.在不同数据库中执行 LINQ 查询以及创建动态查询. 表达式树还能用于动态语言运行时 (DLR) 以提供

C#秘密武器之表达式树

一.表达式树入门 Lambda表达式树很复杂,从概念上很难理解清楚,一句话,表达式树是一种数据结构!这里我们通过下面的这个例子来理解一下表达式树,你就能看个大概: lambda表达式树动态创建方法 static void Main(string[] args) { //i*j+w*x ParameterExpression a = Expression.Parameter(typeof(int),"i"); //创建一个表达式树中的参数,作为一个节点,这里是最下层的节点 Paramet