.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<intintint>。

创建表达式树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,或者是表示方法调用的 MethodCallExpressionParameterExpression 名称空间还解释了 MethodCallExpressionSystem.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 - 动态语言的更多相关文章

  1. 深入学习C#匿名函数、委托、Lambda表达式、表达式树类型——Expression tree types

    匿名函数 匿名函数(Anonymous Function)是表示“内联”方法定义的表达式.匿名函数本身及其内部没有值或者类型,但是可以转换为兼容的委托或者表达式树类型(了解详情).匿名函数转换的计算取 ...

  2. 表达式树(Expression Tree)

    你每创建一个表示表达式的实例时,都可以将该类型实例看成是一棵表达式树.每种表示表达式的类型都有一个具体的类型,如Expression的Variable()方法创建的是ParameterExpressi ...

  3. 表达式树(Expression Trees)

    [翻译]表达式树(Expression Trees) 原文地址:https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/con ...

  4. C# 表达式树(Expression)

    c#中有Expression,即表达式. 通过Expression可以动态构造代码,并编译执行.  比如: 1.  创建参数表达式 :ParameterExpression numParam = Ex ...

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

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

  6. C# 知识回顾 - 表达式树 Expression Trees

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

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

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

  8. C# - LINQ 表达式树

    表达式树(Expression Tree) 表达式树是不可执行的代码,它只是用于表示一种树状的数据结构,树上的每一个节点都表示为某种表达式类型,大概有25种表达式类型,它们都派生自Expression ...

  9. 【C#复习总结】细说表达式树

    1 前言 系类1:细说委托 系类2:细说匿名方法 系列3:细说Lambda表达式 系列4:细说泛型委托 系列5:细说表达式树 系列6:细说事件 涛声依旧,再续前言,接着用大佬的文章作为开头. 表达式树 ...

随机推荐

  1. uniapp如何生成自己的小程序码并且携带参数

    生成小程序码需要用到的参数appId  appSecret这两个参数可以再微信公众平台里面登录获取 也可以用测试号里面的获取小程序码步骤1.首先要请求官方的API`https://api.weixin ...

  2. 阿里巴巴如何进行测试提效 | 阿里巴巴DevOps实践指南

    编者按:本文源自阿里云云效团队出品的<阿里巴巴DevOps实践指南>,扫描上方二维码或前往:https://developer.aliyun.com/topic/devops,下载完整版电 ...

  3. 报错org.springframework.beans.factory.CannotLoadBeanClassException: Error loading class [org.mybatis.spring.SqlSessionFactoryBean]

    超级大坑 org.springframework.beans.factory.CannotLoadBeanClassException: Error loading class [org.mybati ...

  4. python网络爬虫-入门(一)

    前言 1.爬虫程序是Dt(Data Technology,数据技术)收集信息的基础,爬取到目标网站的资料后,就可以分析和建立应用了. 2.python是一个简单.有效的语言,爬虫所需要的获取.存储.整 ...

  5. 使用require.context实现前端工程自动化

    require.context是什么 一个webpack的api,通过执行require.context函数获取一个特定的上下文,主要用来实现自动化导入模块,在前端工程中,如果遇到从一个文件夹引入很多 ...

  6. 洛谷P4859 已经没有什么好害怕的了

    因为不存在任意两个数相同,那么设糖果比药片大的组有 \(x\) 个,药片比糖果大的组有 \(y\) 个,那么我们有: \[x + y = n, x - y = k \] 即: \[x = \frac{ ...

  7. AFN框架

    0.AFN框架基本使用 0.1 AFN内部结构 AFN结构体 - NSURLConnection + AFURLConnectionOperation(已经被废弃) + AFHTTPRequestOp ...

  8. background-position:color url(image) -left -up no-repeat;

    转载请注明来源:https://www.cnblogs.com/hookjc/ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transit ...

  9. UIKit坐标系

    在UIKit中,坐标系的原点(0,0)在左上角,x值向右正向延伸,y值向下正向延伸

  10. iOS 启动画面 App图标尺寸 项目上架必须Icon设置

    1.1AppIcon图标尺寸如下: 说明:AppIcon (6张) AppStore Icon (1张)(png格式) AppStore Icon --- 1024x1024(必须)   1.2启动画 ...