【C#表达式树 开篇】 Expression Tree - 动态语言
.NET 3.5中新增的表达式树(Expression Tree)特性,第一次在.NET平台中引入了“逻辑即数据”的概念。也就是说,我们可以在代码里使用高级语言的形式编写一段逻辑,但是这段逻辑最终会被保存为数据。正因为如此,我们可以使用各种不同的方法对它进行处理。例如,您可以将其转化为一个SQL查询,或者外部服务调用等等,这便是LINQ to Everything在技术实现上的重要基石之一。
学习表达式目录树的目的:
1、之所以学习表达式树的相关知识点主要是为了能进一步理解linq to sql相关执行原理
2、表达式树还能用于动态语言运行时 (DLR) 以提供动态语言和 .NET Framework 之间的互操作性。
3、表达式具有缓存性,可以用与替代反射
在学习表达式目录树前我们要回顾一下知识点:
【C# 基础概念】表达式(expression)、语句(statement)、块(block),指令(using)
任何表达式都有值和类型两个基本属性。
表达式的每个节点都是表达式,所以即使是常量都要封装成几点,然后添加的树中
进入正题。。。。。。。。。。。。
表达式树是什么?
表达式树事一个二叉树,c#引入表达式树,用来储存动态代码,因此C#中的表达式树是一种数据结构,这个数据结构用来存储代码。
我们把它当成另外一种动态语言学习就好了,它也有常量、参数、运算、判断等操作。
下一节我们介绍到expression类的时候就就会接触到它各种函数。
表达式树是不可变对象(immutable),跟string类似,不能直接修改,只能复制一个然后重新构造。具体参考MSDN How to modify expression trees (C#).
表达式树的创建有 Lambda法 和 API组装法。
表达式树 (C#)
表达式树以树形数据结构表示代码,其中每一个节点都是一种表达式,比如方法调用和 x < y
这样的二元运算等。
你可以对表达式树中的代码进行编辑和运算。 这样能够动态修改可执行代码、在不同数据库中执行 LINQ 查询以及创建动态查询。 有关 LINQ 中表达式树的详细信息,请参阅如何使用表达式树生成动态查询 (C#)。
表达式树还能用于动态语言运行时 (DLR) 以提供动态语言和 .NET 之间的互操作性,同时保证编译器编写员能够发射表达式树而非 Microsoft 中间语言 (MSIL)。 有关 DLR 的详细信息,请参阅动态语言运行时概述。
你可以基于匿名 lambda 表达式通过 C# 或者 Visual Basic 编译器创建表达式树,或者通过 System.Linq.Expressions 名称空间手动创建。
表达式树的组成部分
- Body: 得到表达式的主体。
- Parameters: 得到lambda表达式的参数.
- NodeType: 获取树的节点的ExpressionType。共85种不同值,包含所有表达式节点各种可能的类型,例如返回常量,例如返回参数,例如取两个值的小值(<),例如取两个值的大值(>),例如将值相加(+),等等。
- Type: 获取表达式的一个静态类型。在这个例子里,表达式的类型是Func<int, int, int>。
创建表达式树2种方式
1、根据 Lambda 表达式创建表达式树
若 lambda 表达式被分配给 Expression<TDelegate> 类型的变量,则编译器可以发射代码以创建表示该 lambda 表达式的表达式树。
下列代码示例展示如何通过 C# 编译器创建表示 Lambda 表达式 num => num < 5
的表达式树。
Expression<Func<int, bool>> lambda = num => num < 5;
2、通过 API 创建表达式树
通过 API 创建表达式树需要使用 Expression 类。 类包含创建特定类型表达式树节点的静态工厂方法,比如表示参数变量的 ParameterExpression,或者是表示方法调用的 MethodCallExpression。 ParameterExpression 名称空间还解释了 MethodCallExpression、System.Linq.Expressions和另一种具体表达式类型。 这些类型来源于抽象类型 Expression。
下列代码示例展示如何使用 API 创建表示 Lambda 表达式 num => num < 5
的表达式树。
// Add the following using directive to your code file:
// using System.Linq.Expressions; // Manually build the expression tree for
// the lambda expression num => num < 5.
ParameterExpression numParam = Expression.Parameter(typeof(int), "num");
ConstantExpression five = Expression.Constant(5, typeof(int));
BinaryExpression numLessThanFive = Expression.LessThan(numParam, five);
Expression<Func<int, bool>> lambda1 =
Expression.Lambda<Func<int, bool>>(
numLessThanFive,
new ParameterExpression[] { numParam });
API的方式创建更复杂的表达式树
在 .NET Framework 4 或更高版本中,表达式树 API 还支持赋值表达式和控制流表达式,例如循环、条件块和 try-catch
块等。 相对于通过 C# 编译器和 Lambda 表达式创建表达式树,还可利用 API 创建更加复杂的表达式树。 下列示例展示如何创建计算数字阶乘的表达式树。
// Creating a parameter expression.
ParameterExpression value = Expression.Parameter(typeof(int), "value"); // Creating an expression to hold a local variable.
ParameterExpression result = Expression.Parameter(typeof(int), "result"); // Creating a label to jump to from a loop.
LabelTarget label = Expression.Label(typeof(int)); // Creating a method body.
BlockExpression block = Expression.Block(
// Adding a local variable.
new[] { result },
// Assigning a constant to a local variable: result = 1
Expression.Assign(result, Expression.Constant(1)),
// Adding a loop.
Expression.Loop(
// Adding a conditional block into the loop.
Expression.IfThenElse(
// Condition: value > 1
Expression.GreaterThan(value, Expression.Constant(1)),
// If true: result *= value --
Expression.MultiplyAssign(result,
Expression.PostDecrementAssign(value)),
// If false, exit the loop and go to the label.
Expression.Break(label, result)
),
// Label to jump to.
label
)
); // Compile and execute an expression tree.
int factorial = Expression.Lambda<Func<int, int>>(block, value).Compile()(5); Console.WriteLine(factorial);
// Prints 120.
表达式树永久性
表达式树应具有永久性。 这意味着如果你想修改某个表达式树,则必须复制该表达式树然后替换其中的节点来创建一个新的表达式树。 你可以使用表达式树访问者遍历现有表达式树。 有关详细信息,请参阅如何修改表达式树 (C#)。
编译并且执行表达式树
C# 编译器只能从表达式 Lambda(或单行 Lambda)生成表达式树。 它无法解析语句 lambda (或多行 lambda)。 有关 C# 中 Lambda 表达式的详细信息,请参阅 Lambda 表达式。
表达式树 编译后会生成委托
//创建表达式的语法糖
Expression<Func<int, bool>> expr = num => num < 5; // Compiling the expression tree into a delegate.
Func<int, bool> result = expr.Compile(); // Invoking the delegate and writing the result to the console.
Console.WriteLine(result(4)); //语法糖,编译后执行
Console.WriteLine(expr.Compile()(4));
注意:public Delegate Compile();
LambdaExpression.Compile() 方法返回 Delegate 类型。 必须将其转换为正确的委托类型,以便使任何编译时工具检查参数列表或返回类型。
public sealed class Expression<TDelegate>
{
public TDelegate Compile(){}
}
Expression<Func<int>> add = () => 1 + 2;
var func = add.Compile(); // Create Delegate
该委托类型基于表达式类型。 如果想要以强类型的方式使用委托对象,则必须知道返回类型和参数列表。
生存期
通过调用在调用 LambdaExpression.Compile()
时创建的委托来执行代码。 可以在上面进行查看,其中 add.Compile()
返回了一个委托。 通过调用 func()
调用该委托将执行代码。
该委托表示表达式树中的代码。 可以保留该委托的句柄并在稍后调用它。 不需要在每次想要执行表达式树所表示的代码时编译表达式树。 (请记住,表达式树是不可变的,且在之后编译同一表达式树将创建执行相同代码的委托。)
注意事项
注意闭包问题,要保证被lambda捕获的局部变量,在在表达式树生成的的委托 是都可用
https://docs.microsoft.com/zh-cn/dotnet/csharp/expression-trees-execution
与表达式树相关的类介绍
Expressions 表达式的命名空间
Expression 表达式抽象基类通常做继承
Expression<T>=>LambdaExpression=>Expression=>Object 用来接收LambdaExpression.ComPile 返回值。
ExpressionType:表达式树的节点类型枚举
在这里插入图片描述
当然还有没有直接继承Expression的,比如:Expression<T>(我们最常用的表达式树)的继承关系为:Expression<T>=>LambdaExpression=>Expression=>Object,这里我就挑选几个表达式树简单描述一下:
BinaryExpression:二元运算表达式
1). Right:二元运算符的右侧表达式(Expression)
2). Left:二元运算符的左侧表达式(Expression)
ConstantExpression:常量表达式
1). Value:常量值,Object
ConditionalExpression:条件表达式
1). IfFalse:为False时的表达式(Expression)
2). IfTrue:为True时的表达式(Expression)
3). Test:判断表达式
ParameterExpression:参数表达式
1). IsByRef:参数是否传引用
2). Name:参数名称
LambdaExpression:lambda表达式
1).Body:内容表达式(Expression)
2).Parameters:参数表达式集合(ReadOnlyCollection)
3).ReturnType:返回的类型
4).Compile():方法,编译生成委托
Expression<T>:带有泛型的表达式,一般这个T就是委托类型的,继承自LabelExpression
【C#表达式树 开篇】 Expression Tree - 动态语言的更多相关文章
- 深入学习C#匿名函数、委托、Lambda表达式、表达式树类型——Expression tree types
匿名函数 匿名函数(Anonymous Function)是表示“内联”方法定义的表达式.匿名函数本身及其内部没有值或者类型,但是可以转换为兼容的委托或者表达式树类型(了解详情).匿名函数转换的计算取 ...
- 表达式树(Expression Tree)
你每创建一个表示表达式的实例时,都可以将该类型实例看成是一棵表达式树.每种表示表达式的类型都有一个具体的类型,如Expression的Variable()方法创建的是ParameterExpressi ...
- 表达式树(Expression Trees)
[翻译]表达式树(Expression Trees) 原文地址:https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/con ...
- C# 表达式树(Expression)
c#中有Expression,即表达式. 通过Expression可以动态构造代码,并编译执行. 比如: 1. 创建参数表达式 :ParameterExpression numParam = Ex ...
- [C#] C# 知识回顾 - 表达式树 Expression Trees
C# 知识回顾 - 表达式树 Expression Trees 目录 简介 Lambda 表达式创建表达式树 API 创建表达式树 解析表达式树 表达式树的永久性 编译表达式树 执行表达式树 修改表达 ...
- C# 知识回顾 - 表达式树 Expression Trees
C# 知识回顾 - 表达式树 Expression Trees 目录 简介 Lambda 表达式创建表达式树 API 创建表达式树 解析表达式树 表达式树的永久性 编译表达式树 执行表达式树 修改表达 ...
- [.net 面向对象程序设计进阶] (6) Lamda表达式(二) 表达式树快速入门
[.net 面向对象程序设计进阶] (6) Lamda表达式(二) 表达式树快速入门 本节导读: 认识表达式树(Expression Tree),学习使用Lambda创建表达式树,解析表达式树. 学习 ...
- C# - LINQ 表达式树
表达式树(Expression Tree) 表达式树是不可执行的代码,它只是用于表示一种树状的数据结构,树上的每一个节点都表示为某种表达式类型,大概有25种表达式类型,它们都派生自Expression ...
- 【C#复习总结】细说表达式树
1 前言 系类1:细说委托 系类2:细说匿名方法 系列3:细说Lambda表达式 系列4:细说泛型委托 系列5:细说表达式树 系列6:细说事件 涛声依旧,再续前言,接着用大佬的文章作为开头. 表达式树 ...
随机推荐
- cesium结合geoserver利用WFS服务实现图层删除(附源码下载)
前言 cesium 官网的api文档介绍地址cesium官网api,里面详细的介绍 cesium 各个类的介绍,还有就是在线例子:cesium 官网在线例子,这个也是学习 cesium 的好素材. 内 ...
- MyCms 自媒体 CMS 系统 v2.8,支持织梦数据导入
MyCms 是一款基于Laravel开发的开源免费的自媒体博客CMS系统,助力开发者知识技能变现. MyCms 基于Apache2.0开源协议发布,免费且不限制商业使用,欢迎持续关注我们. V2.8 ...
- zabbix安装 报错 socket '/var/lib/mysql/mysql.sock' (13)]
安装界面提示: Error connecting to database: Can't connect to local MySQL server through socket '/var/lib/m ...
- 利用JavaWeb实现课程信息添加
整体架构 :HTML+JAVABEAN+SERVLET 一.首先先简单介绍一下所需要的组件 原文地址 https://www.cnblogs.com/zll20153246/p/ ...
- django之django-debug-toolbar调试工具配置与使用
外部链接:https://blog.csdn.net/weixin_39198406/article/details/78821677 django-debug-toolbar的作用:进行性能调优,与 ...
- python编写购物车
上次的学习又没有坚持下来,工作忙的不可开交,但我反思了一下还是自己没有下定决心好好学习,所以这次为期3个月的学习计划开始了,下面是这次学习后重新编写的购物车初版代码. 1 # 功能要求: 2 # 要求 ...
- 在java中静态方法与非静态方法
在java中public void与public static void有什么区别 ? public void 修饰是非静态方法,该类方法属于对象,在对象初始化(new Object())后才能被调用 ...
- Android利用zxing生成二维码
感谢大佬:https://blog.csdn.net/mountain_hua/article/details/80646089 **gayhub上的zxing可用于生成二维码,识别二维码 gayhu ...
- UIView属性的讲解
1.父控件和子控件的理解在storyboard中只有UIView是可以在里面拖入子控件的,其他控件不可以(必须通过代码添加)拖入一个UIView控件,在里面添加一些子控件(UIView控件是控制器的V ...
- 一键部署lnmp
一键部署lnmp 提前将nginx .mysql .php 所需安装包都放在/opt目录下 脚本启动结束时,重启一下nginx 服务,就能在火狐浏览器更新出php测试页 脚本如下:(脚本里的软件 ...