首页 > 其他分享 >表达式树

表达式树

时间:2023-08-12 22:22:16浏览次数:38  
标签:反射 代码 LINQ 编译 可以 表达式

这节来讲一下C#中的表达式树(又称表达式目录树、Expression)。

什么是表达式树?

 表达式树是一种C#中的数据结构,它以树的形式表示某些代码内部的结构。每个节点是一种称为表达式的C#对象,例如二元运算,方法调用,常量等。这种数据结构主要用于LINQ查询的内部机制和动态编程。在C#中,表达式树使在编译时表达式的结构和操作被保留下来,而不是像通常的.net代码那样被直接编译成IL。这使得你可以在运行时操作这些表达式或将它们转换成其他形式。例如,你可以将一个表达式树转换为可重用的Lambda表达式,或者用于创建动态查询。或者,你可以遍历表达式树来读取和解析表达式的结构。这种技术是.NET Framework中LINQ的基础,特别是在使用LINQ to SQL和LINQ to Entities时,因为它允许在运行时将LINQ查询表达式转换为SQL查询。

表达式树、lambda、委托三者区别

表达式树、lambda、委托看似很像,写代码的时候也经常配套使用,那三者有什么区别呢?

1. 委托:在C#中,委托(Delegate)是一种类型安全的函数指针,它定义了可以代表的方法的类型。这允许你将方法作为参数传递,或者将方法存储在变量中。它是.NET事件处理的基础。

2. lambda表达式:lambda表达式是创建委托或表达式树类型的一种便捷方式。通过使用lambda表达式,你可以编写局部函数,这些函数可以在表达式或语句的上下文中使用。lambda表达式是匿名的,它们不具有特定的名称。

3. 表达式树:表达式树是一种特殊的数据结构,主要用于表示和处理代码以数据的形式。它们常常用于创建动态查询和解析、处理和执行命令模式。表达式树可以从lambda表达式创建,然后可以被编译并执行。总的来说,lambda表达式是创建表达式树和委托实例的一种方式,委托是一种可以引用方法的类型,而表达式树则提供了一种灵活处理代码的方式,使得你可以在运行时操作和执行代码。

代码演示

    public class Program
    {
        public static void Main()
        {
            // 参数表达式
            ParameterExpression numParam = Expression.Parameter(typeof(int), "num");
            // 常数表达式
            ConstantExpression five = Expression.Constant(5, typeof(int));
            // 比较表达式: num > 5
            BinaryExpression numGreaterThanFive = Expression.GreaterThan(numParam, five);
            // Lambda表达式
            LambdaExpression lambda1 = Expression.Lambda(numGreaterThanFive, new ParameterExpression[] { numParam }); 
            // 编译并执行
            Console.WriteLine("num > 5: " + ((Func<int, bool>)lambda1.Compile())(6)); // 输出: num > 5: True
        }
    }

在这个示例中,我们创建了一个表达式树来表示 "num > 5" 运算。然后,我们把这个表达式树转换为一个Lambda表达式,并且编译并运行这个Lambda表达式,输出其结果。

反射与表达式树

在.NET中,表达式树和反射都可以用来在运行时动态地生成和执行代码。然而,表达式树提供了一种在执行效率和代码清晰度方面更优的选择。

反射是.NET框架提供的一种功能,它允许我们在运行时获取类型的信息,创建对象,调用方法,获取和设置字段/属性的值等。然而,反射的缺点在于它的执行效率不高,因为它需要在运行时解析类型信息。此外,反射代码往往难以阅读和维护。

而表达式树实际上是一个数据结构,它以树形式表示代码。我们可以创建和修改表达式树,然后将其编译为委托并执行。表达式树的主要优点在于它们可以在运行时生成和编译,从而提供了比反射更高的执行效率。此外,表达式树的代码通常比反射代码更清晰,更易于理解。例如,假设我们需要动态地调用一个对象的方法。使用反射,我们需要获取类型的信息,查找方法,创建参数,并调用方法。使用表达式树,我们可以创建一个表示该方法调用的表达式树,然后将其编译为委托并执行。因此,虽然表达式树和反射都可以在运行时动态地生成和执行代码,但在很多情况下,表达式树提供了一种效率更高、代码更清晰的选择。

下面通过一个例子来比较一下如何通过反射和表达式树访问对象的属性。

有如下一个类:

    public class Person
    {
        public string Name { get; set; }
    }

使用反射读取其Name属性:

Person person = new Person { Name = "川建国" };Type type = typeof(Person);
PropertyInfo propertyInfo = type.GetProperty("Name");string name = (string)propertyInfo.GetValue(person);
Console.WriteLine(name); // 输出: 川建国

使用表达式树形式读取其Name属性:

Person person = new Person { Name = "川建国" };
ParameterExpression objExp = Expression.Parameter(typeof(Person), "p");
MemberExpression propertyExp = Expression.Property(objExp, "Name");
LambdaExpression lambdaExp = Expression.Lambda(propertyExp, objExp);
Func<Person, string> getPersonName = (Func<Person, string>)lambdaExp.Compile();
string name = getPersonName(person);
Console.WriteLine(name); // 输出: 川建国

  

我们可以看到,虽然表达式树的代码看起来更复杂一些,但实际上它运行得更快,特别是在需要重复执行的情况下,因为编译过的委托可以重复使用,而反射每次都需要重新解析类型信息和方法信息。

最后,概述一下表达式的特点

1. 表达式树是代码的数据结构表示:与直接编写的代码不同,表达式树是代码的数据结构表示。它让你可以在运行时检查和操作数据,就像操作其他数据结构一样。 

2. 表达式树可以被动态生成:这是表达式树的一个重要特性,你可以在运行时动态创建和修改表达式树。这对于需要动态生成和执行代码的场景(例如,LINQ提供者)非常有用。

3. 表达式树可以被编译并执行:表达式树不仅可以表示代码,还可以被编译并执行。这使得表达式树比反射有更好的性能,因为反射需要在运行时解析类型和方法信息,而表达式树在编译后就可以直接执行。 

4. 表达式树可以用于创建LINQ查询:LINQ查询实际上就是表达式树。当你写一个LINQ查询时,编译器实际上是在后台创建一个表达式树。这个表达式树然后可以被LINQ提供者(如Entity Framework)用来生成和执行相应的SQL查询。 

5. 表达式树可以用于序列化和反序列化表达式:由于表达式树是代码的数据结构表示,你可以将其序列化为二进制或文本格式,然后在另一个上下文(甚至在另一个进程或机器)中反序列化并执行。这对于远程过程调用(RPC)和分布式计算等场景非常有用。

 

标签:反射,代码,LINQ,编译,可以,表达式
From: https://www.cnblogs.com/charlesmvp/p/17625651.html

相关文章

  • lambda表达式(jdk8才开始出现的语法)
    1、是为了简化某些场景下匿名对象的繁琐。其中有一种函数式编程(强调做什么,而不是强调谁去做)的思想。语法格式:(形参列表)->(固定格式){​ 方法体;}测试代码如下所示:importjava.util.Arrays;importjava.util.Comparator;publicclassTest{publicstaticvoidmain(S......
  • 正则表达式学习笔记
    .:任意一个字符\d:代表一个数字,等价于[0-9]\D:代表一个非数字,等价于[^\d]或者[^0-9]\s:代表一个空白字符,诸如Space,\n,\r,Tab\S:代表一个非空白字符\w:代表一个单词字符,诸如a,9,_,蛙\W:代表一个非单词字符*:量词,左侧字符串出现任意次(包括\(0\)次)?:量词,左侧字符出现\(\le1\)次+:......
  • 表达式计算通用规则
    表达式计算通用规则从左向右运算符俩俩比较,左边运算符优先级高于右边运算符时,先算左边的。左边运算符优先级低于右边的继续向右比较,直到找到一个相对最高的。(之后的的运算符低,或者到末尾了),进行运算。如果左右优先级相同看结合性,(单目运算右结合,双目运算左结合。三目运算嵌套......
  • Shell 条件表达式的使用
    介绍条件表达式可以是一元的,也可以是二元。一元表达式常用于检查文件状态、字符串运算、数字运算。使用检查文件状态参数-a文件存在,则为真(TRUE)。-b文件存在并且是块设备,则为真(TRUE)。-c文件存在并且是字符设备,则为真(TRUE)。-d文件存在并且是目录,则为真(TRUE)。-e文件......
  • 【面试题】 JavaScript中高级语法--?? 表达式 的作用
    前言在JavaScript中,双问号(??)表达式是一种非常有用的方法。它的作用是用来检测一个值是否为null或undefined。如果该值为null或undefined,那么双问号表达式会返回一个默认值。下面我们就来具体探究一下双问号表达式的用法,以及它与其他相似方法的区别。具体用法。其实,双问号表达式就是......
  • BootstrapBlazor组件库,组件方法的表达式传参
    BootstrapBlazor组件库,组件方法的表达式传参有时候我们在循环中创建组件的时候,可能想把item的值也一并传入组件的方法事件中去处理,有很多小伙伴就不知道如何去调用事件了。下面是一段实例代码,通过循环遍历来创建图片,并且创建一个删除图片的按钮,这个按钮的OnConfirm方法需要把图......
  • 正则表达式
    24小时制时间(HH:mm:ss)/^((?:[01]d|2[0-3]):[0-5]d:[0-5]d$)/12小时制时间(hh:mm:ss)/^(1[0-2]|0?[1-9]):[0-5]d:[0-5]d$/base64格式/^s*data:([a-z]+/[a-z0-9-+.]+(;[a-z-]+=[a-z0-9-]+)?)?(;base64)?,([a-z0-9!$&',()*+;=-._~:@/?%s]*?)s*$/i数字/货币金额(支持负数、千......
  • Go语言正则表达式提取网页文本
    为了方便提取,我们会把正则表达式中要提取的数据使用命名方式来书写正则表达式。这个技术在Go语言中如何实现,可以看下面这篇博客:UsingtheGoRegexpPackagehttp://blog.kamilkisiel.net/blog/2012/07/05/using-the-go-regexp-package/简单期间,这里复制其中几个例子的代码:我们期望......
  • 【Nginx用法】nginx location正则表达式写法,详解Nginx location 匹配规则(很详细哦)
    本文目录一、常用规则 二、实际使用建议三、Flag标志位四、If判断指令五、全局变量六、常用正则七、Rewrite规则八、Rewrite实例8.1实例一8.2实例二九、项目实例9.1项目一9.2项目实战作为一名Java开发人员,有些东西不经常使用,很容易忘记,好比nginx配置内容,以下内容是记录了公司......
  • python正则表达式笔记1
    最近工作中经常用到正则表达式处理数据,慢慢发现了正则表达式的强大功能,尤其在数据处理工作中,记录下来分享给大家。一、正则表达式语法介绍正则表达式(或RE)指定了一组与之匹配的字符串;模块内的函数可以检查某个字符串是否与给定的正则表达式匹配(或者正则表达式是否匹配到字符串,......