一、前言

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

表达式系列目录

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);

运行结果:

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

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

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

  1. C# 动态构建表达式树(二)——构建 Select 和 GroupBy 的表达式

    C# 动态构建表达式树(二)--构建 Select 和 GroupBy 的表达式 前言 在上篇中写了表达式的基本使用,为 Where 方法动态构建了表达式.在这篇中会写如何为 Select 和 Gro ...

  2. C# 表达式树讲解(一)

    一.前言 一直想写一篇Dpper的定制化扩展的文章,但是里面会设计到对Lambda表达式的解析,而解析Lambda表达式,就必须要知道表达式树的相关知识点.我希望能通过对各个模块的知识点或者运用能够多 ...

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

    一.前言 前面我们知道了表达树的基本知识,也明白了怎么遍历和修改一个表达式,这里我们就一个实际的场景来进行功能开发. 表达式系列目录 C# 表达式树讲解(一) C# 表达式树遍历(二) C# 表达式树 ...

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

    一.前言 本来计算这篇文章在后面需要运用的时候写的,但是既然写到表达式的扩展呢,就一起写完吧. 看到这个标题就有一种疑问,Lambda表达式本来就是表达式树,还需要怎么扩展?那就看看下面的内容,你就知 ...

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

    当我们知道了Linq查询要用到的数据库信息之后.接下就是生成对应的表达式树.在前面的章节里面笔者就已经介绍过.生成表达式树是事实离不开IQueryable<T>接口.而处理表达式树离不开I ...

  6. 不可不知的表达式树(1)Expression初探

    说起Lambda表达式,大家基本都很熟悉了,而表达式树(Expression Trees),则属于80%的工作中往往都用不到的那种技术,所以即便不是什么新技术,很多人对其理解都并不透彻.此文意图从表达 ...

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

    [.net 面向对象程序设计进阶] (6) Lamda表达式(二) 表达式树快速入门 本节导读: 认识表达式树(Expression Tree),学习使用Lambda创建表达式树,解析表达式树. 学习 ...

  8. Hdu1805-Expression(表达式树模版题+层序遍历树+栈的基本应用)

    2018-11-23-02:27:37 原题链接 题目描述: 题目一目了然. 本题思路: 本题很容易能想到是构建表达式树然后按照层序逆序输出即可. AC代码: #include <cstdio& ...

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

    C# 知识回顾 - 表达式树 Expression Trees 目录 简介 Lambda 表达式创建表达式树 API 创建表达式树 解析表达式树 表达式树的永久性 编译表达式树 执行表达式树 修改表达 ...

随机推荐

  1. JVM总结(三)

    JVM总结(3)Class文件,类加载机制.编译过程 Java编译器先把Java代码编译为存储字节码的Class文件,再通过Class文件进行类加载. Class类文件的结构 Java编译器可以把Ja ...

  2. iview自定义实现多级表头

    最近更新: 2018-07-19 注意:最新版iview已经提供多级表头功能 参考 原理:利用多个Table组件通过显示和隐藏thead和tbody来拼接表格(较粗暴) html <div st ...

  3. mysql主从不同步处理过程分享

    背景  8月7日15:58收到报障数据库出现不同步:数据库共四台,分别为10.255.70.11,10.255.70.12,10.255.70.13,10.255.70.14(ip为虚拟ip) 数据库 ...

  4. socket基于TCP(粘包现象和处理)

    目录 6socket套接字 7基于TCP协议的socket简单的网络通信 AF_UNIX AF_INET(应用最广泛的一个) 报错类型 单一 链接+循环通信 远程命令 9.tcp 实例:远程执行命令 ...

  5. 什么是CWS、WBS、OBS

    今天公司进行CMMI资质审核,审核人提到了WBS,以前对这些名词没有太过于注意,后经过审核人的审核对这个名词有了一个大致的了解,并结合项目经验和网上的一些资料,编此文档.不为别人,主要怕自己忘记了. ...

  6. Oracle cursor学习笔记

    目录 一.oracle库缓存 1.1.库缓存简介 1.2.相关概念 1.3.库缓存结构 1.4.sql执行过程简介 二.oracle cursor 2.1.cursor分类 2.2.shared cu ...

  7. Docker搭建Zookeeper&Kafka集群

    最近在学习Kafka,准备测试集群状态的时候感觉无论是开三台虚拟机或者在一台虚拟机开辟三个不同的端口号都太麻烦了(嗯..主要是懒). 环境准备 一台可以上网且有CentOS7虚拟机的电脑 为什么使用虚 ...

  8. python 37 同步、异步调用

    目录 1. 阻塞与非阻塞 2. 同步与异步 2.1 异步调用 2.2 同步调用 2.3 异步调用回收的第一种方式 3. 异步调用+回调函数 3.1 requests模块 3.2 异步调用回收的第二种方 ...

  9. Java集合框架之Vector浅析

    Java集合框架之Vector浅析 一.Vector概述: 位于java.util包下的Vector是Java集合框架的重要一员,虽然没有ArrayList那么的常用,但是我们还要对其做相关学习: 1 ...

  10. Java基础之Iterable与Iterator

    Java基础之Iterable与Iterator 一.前言: Iterable :故名思议,实现了这个接口的集合对象支持迭代,是可迭代的.able结尾的表示 能...样,可以做.... Iterato ...