C# 表达式树Expression Trees的知识梳理
目录
- 简介
- Lambda 表达式创建表达式树
- API 创建表达式树
- 解析表达式树
- 表达式树的永久性
- 编译表达式树
- 执行表达式树
- 修改表达式树
- 调试
简介
表达式树以树形数据结构表示代码,其中每一个节点都是一种表达式,比如方法调用和 x < y 这样的二元运算等。
你可以对表达式树中的代码进行编辑和运算。这样能够动态修改可执行代码、在不同数据库中执行 LINQ 查询以及创建动态查询。
表达式树还能用于动态语言运行时 (DLR) 以提供动态语言和 .NET Framework 之间的互操作性。
一、Lambda 表达式创建表达式树
若 lambda 表达式被分配给 Expression<TDelegate> 类型的变量,则编译器可以发射代码以创建表示该 lambda 表达式的表达式树。
C# 编译器只能从表达式 lambda (或单行 lambda)生成表达式树。
下列代码示例使用关键字 Expression创建表示 lambda 表达式:
Expression<Action<int>> actionExpression = n => Console.WriteLine(n); Expression<Func<int, bool>> funcExpression1 = (n) => n < 0; Expression<Func<int, int, bool>> funcExpression2 = (n, m) => n - m == 0;
二、API 创建表达式树
通过 API 创建表达式树需要使用Expression 类
下列代码示例展示如何通过 API 创建表示 lambda 表达式:num => num == 0
//通过 Expression 类创建表达式树 // lambda:num => num == 0 ParameterExpression pExpression = Expression.Parameter(typeof(int)); //参数:num ConstantExpression cExpression = Expression.Constant(0); //常量:0 BinaryExpression bExpression = Expression.MakeBinary(ExpressionType.Equal, pExpression, cExpression); //表达式:num == 0 Expression<Func<int, bool>> lambda = Expression.Lambda<Func<int, bool>>(bExpression, pExpression); //lambda 表达式:num => num == 0
代码使用Expression 类的静态方法进行创建。
三、解析表达式树
下列代码示例展示如何分解表示 lambda 表达式 num => num == 0 的表达式树。
Expression<Func<int, bool>> funcExpression = num => num == 0; //开始解析 ParameterExpression pExpression = funcExpression.Parameters[0]; //lambda 表达式参数 BinaryExpression body = (BinaryExpression)funcExpression.Body; //lambda 表达式主体:num == 0 Console.WriteLine($"解析:{pExpression.Name} => {body.Left} {body.NodeType} {body.Right}");
四、表达式树永久性
表达式树应具有永久性(类似字符串)。这意味着如果你想修改某个表达式树,则必须复制该表达式树然后替换其中的节点来创建一个新的表达式树。 你可以使用表达式树访问者遍历现有表达式树。第七节介绍了如何修改表达式树。
五、编译表达式树
Expression<TDelegate> 类型提供了 Compile 方法以将表达式树表示的代码编译成可执行委托。
//创建表达式树 Expression<Func<string, int>> funcExpression = msg => msg.Length; //表达式树编译成委托 var lambda = funcExpression.Compile(); //调用委托 Console.WriteLine(lambda("Hello, World!")); //语法简化 Console.WriteLine(funcExpression.Compile()("Hello, World!"));
六、执行表达式树
执行表达式树可能会返回一个值,也可能仅执行一个操作(例如调用方法)。
只能执行表示 lambda 表达式的表达式树。表示 lambda 表达式的表达式树属于 LambdaExpression 或 Expression<TDelegate> 类型。若要执行这些表达式树,需要调用 Compile 方法来创建一个可执行委托,然后调用该委托。
const int n = 1; const int m = 2; //待执行的表达式树 BinaryExpression bExpression = Expression.Add(Expression.Constant(n), Expression.Constant(m)); //创建 lambda 表达式 Expression<Func<int>> funcExpression = Expression.Lambda<Func<int>>(bExpression); //编译 lambda 表达式 Func<int> func = funcExpression.Compile(); //执行 lambda 表达式 Console.WriteLine($"{n} + {m} = {func()}");
七、修改表达式树
该类继承 ExpressionVisitor 类,通过 Visit 方法间接调用 VisitBinary 方法将 != 替换成 ==。基类方法构造类似于传入的表达式树的节点,但这些节点将其子目录树替换为访问器递归生成的表达式树。
internal class Program { private static void Main(string[] args) { Expression<Func<int, bool>> funcExpression = num => num == 0; Console.WriteLine($"Source: {funcExpression}"); var visitor = new NotEqualExpressionVisitor(); var expression = visitor.Visit(funcExpression); Console.WriteLine($"Modify: {expression}"); Console.Read(); } /// <summary> /// 不等表达式树访问器 /// </summary> public class NotEqualExpressionVisitor : ExpressionVisitor { public Expression Visit(BinaryExpression node) { return VisitBinary(node); } protected override Expression VisitBinary(BinaryExpression node) { return node.NodeType == ExpressionType.Equal ? Expression.MakeBinary(ExpressionType.NotEqual, node.Left, node.Right) //重新弄个表达式:用 != 代替 == : base.VisitBinary(node); } } }
八、调试
8.1 参数表达式
ParameterExpression pExpression1 = Expression.Parameter(typeof(string)); ParameterExpression pExpression2 = Expression.Parameter(typeof(string), "msg");
图8-1
图8-2
从 DebugView 可知,如果参数没有名称,则会为其分配一个自动生成的名称。
const int num1 = 250; const float num2 = 250; ConstantExpression cExpression1 = Expression.Constant(num1); ConstantExpression cExpression2 = Expression.Constant(num2);
图8-3
图8-4
从 DebugView 可知,float 比 int 多了个后缀 F。
Expression lambda1 = Expression.Lambda<Func<int>>(Expression.Constant(250)); Expression lambda2 = Expression.Lambda<Func<int>>(Expression.Constant(250), "CustomName", null);
图8-5
图8-6
观察 DebugView ,如果 lambda 表达式没有名称,则会为其分配一个自动生成的名称。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持我们!
上一篇:C#数组中List, Dictionary的相互转换问题
栏 目:C#教程
本文标题:C# 表达式树Expression Trees的知识梳理
本文地址:https://www.xiuzhanwang.com/a1/C_jiaocheng/6049.html
您可能感兴趣的文章
- 01-10C#编程自学之运算符和表达式
- 01-10C#正则表达式的6个简单例子
- 01-10C#中的正则表达式介绍
- 01-10C#使用正则表达式实现首字母转大写的方法
- 01-10轻松学习C#的正则表达式
- 01-10详解C#正则表达式Regex常用匹配
- 01-10C#通过正则表达式实现提取网页中的图片
- 01-10C#正则表达式Regex类的常用匹配
- 01-10常用C#正则表达式汇总介绍
- 01-10实例详解C#正则表达式
阅读排行
本栏相关
- 01-10C#通过反射获取当前工程中所有窗体并
- 01-10关于ASP网页无法打开的解决方案
- 01-10WinForm限制窗体不能移到屏幕外的方法
- 01-10WinForm绘制圆角的方法
- 01-10C#实现txt定位指定行完整实例
- 01-10WinForm实现仿视频播放器左下角滚动新
- 01-10C#停止线程的方法
- 01-10C#实现清空回收站的方法
- 01-10C#通过重写Panel改变边框颜色与宽度的
- 01-10C#实现读取注册表监控当前操作系统已
随机阅读
- 08-05dedecms(织梦)副栏目数量限制代码修改
- 01-10使用C语言求解扑克牌的顺子及n个骰子
- 01-11ajax实现页面的局部加载
- 08-05DEDE织梦data目录下的sessions文件夹有什
- 01-11Mac OSX 打开原生自带读写NTFS功能(图文
- 04-02jquery与jsp,用jquery
- 01-10SublimeText编译C开发环境设置
- 08-05织梦dedecms什么时候用栏目交叉功能?
- 01-10C#中split用法实例总结
- 01-10delphi制作wav文件的方法