专栏首页.NET技术与企业级解决方案C#3.0新增功能10 表达式树 02 说明

C#3.0新增功能10 表达式树 02 说明

  表达式树是定义代码的数据结构。 它们基于编译器用于分析代码和生成已编译输出的相同结构。表达式树和 Roslyn API 中用于生成分析器和 CodeFixes 的类型之间存在很多相似之处。 (分析器和 CodeFixes 是 NuGet 包,用于对代码执行静态分析,并可为开发人员建议可能的修补程序。)两者概念相似,且最终结果是一种数据结构,该结构允许以有意义的方式对源代码进行检查。 但是,表达式树基于一组与 Roslyn API 完全不同的类和 API。

让我们来举一个简单的示例。 以下是一个代码行:

var sum = 1 + 2;

如果要将其作为一个表达式树进行分析,则该树包含多个节点。 最外面的节点是具有赋值 (var sum = 1 + 2;) 的变量声明语句,该节点包含若干子节点:变量声明、赋值运算符和一个表示等于号右侧的表达式。 该表达式被进一步细分为表示加法运算、该加法左操作数和右操作数的表达式。

让我们稍微深入了解一下构成等于号右侧的表达式。 该表达式是 1 + 2。 这是一个二进制表达式。 更具体地说,它是一个二进制加法表达式。 二进制加法表达式有两个子表达式,表示加法表达式的左侧和右侧节点。 此处的两个节点都是常量表达式:左操作数是值 1,右操作数是值 2

直观地看,整个语句是一个树:应从根节点开始,遍历到树中的每个节点,以查看构成语句的代码:

  • 具有赋值 (var sum = 1 + 2;) 的变量声明语句
    • 隐式变量类型声明 (var sum)赋值运算符 (=)
      • 隐式 var 关键字 (var)
      • 变量名称声明 (sum)
    • 二进制加法表达式 (1 + 2)
      • 左操作数 (1)
      • 加法运算符 (+)
      • 右操作数 (2)

这可能看起来很复杂,但它功能强大。 按照相同的过程,可以分解更加复杂的表达式。 请思考此表达式:

var finalAnswer = this.SecretSauceFunction(currentState.createInterimResult(),
                                           currentState.createSecondValue(1, 2),
                                           decisionServer.considerFinalOptions("hello")
                                           ) 
                  + MoreSecretSauce('A', DateTime.Now, true);

上述表达式也是具有赋值的变量声明。 在此情况下,赋值的右侧是一棵更加复杂的树。 我不打算分解此表达式,但请思考一下不同的节点可能是什么。 存在使用当前对象作为接收方的方法调用,其中一个调用具有显式 this 接收方,一个调用不具有此接收方。 存在使用其他接收方对象的方法调用,存在不同类型的常量参数。 最后,存在二进制加法运算符。 该二进制加法运算符可能是对重写的加法运算符的方法调用(具体取决于 SecretSauceFunction()MoreSecretSauce() 的返回类型),解析为对为类定义的二进制加法运算符的静态方法调用。

尽管具有这种感知上的复杂性,但上面的表达式创建了一种树形结构,可以像第一个示例那样轻松地导航此结构。 可以保持遍历子节点,以查找表达式中的叶节点。 父节点将具有对其子节点的引用,且每个节点均具有一个用于介绍节点类型的属性。

表达式树的结构非常一致。 了解基础知识后,你甚至可以理解以表达式树形式表示的最复杂的代码。 优美的数据结构说明了 C# 编译器如何分析最复杂的 C# 程序并从该复杂的源代码创建正确的输出。

熟悉表达式树的结构后,你会发现通过快速获得的知识,你可处理许多越来越高级的方案。 表达式树的功能非常强大。

除了转换算法以在其他环境中执行之外,表达式树还可用于在执行代码前轻松编写检查代码的算法。 可以编写参数为表达式的方法,然后在执行代码之前检查这些表达式。 表达式树是代码的完整表示形式:可以看到任何子表达式的值。 可以看到方法和属性名称。 可以看到任何常数表达式的值。 还可以将表达式树转换为可执行的委托,并执行代码。

通过表达式树的 API,可创建表示几乎任何有效代码构造的树。 但是,出于尽可能简化的考虑,不能在表达式树中创建某些 C# 习惯用语。 其中一个示例就是异步表达式(使用 asyncawait关键字)。 如果需要异步算法,则需要直接操作 Task 对象,而不是依赖于编译器支持。 另一个示例是创建循环。 通常,通过使用 forforeachwhiledo 循环对其进行创建。 正如稍后可以在本系列中看到的那样,表达式树的 API 支持单个循环表达式,该表达式包含控制重复循环的 breakcontinue 表达式。

不能执行的操作是修改表达式树。 表达式树是不可变的数据结构。 如果想要改变(更改)表达式树,则必须创建基于原始树副本但包含所需更改的新树。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • C# -多线程任务 Task

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

    叉叉敌
  • 科普 | DLL劫持原理与实践

    DLL劫持算是一个老的漏洞,而且乌云漏洞库中也有很多的案例,只不过案例更多的只是验证一下,并没有教如何利用。至于为什么专门抓起来再学一遍了,唉,内网渗透需要

    HACK学习
  • 编程小知识之 struct 构造函数(C#)

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

    用户2615200
  • linux使用find和crontab命令定期清理过期文件

    crontab 命令是 Linux 中用来设定重复执行命令或脚本的工具。它能够在指定的时间段内,按照需求以某一时间间隔执行命令或脚本。

    大道七哥
  • (四十九)c#Winform自定义控件-下拉框(表格)

    GitHub:https://github.com/kwwwvagaa/NetWinformControl

    冰封一夏
  • C#开发学习人工智能的第一步

    作为一个软件开发者,我们除了要学会复制,黏贴,还要学会调用API和优秀的开源类库。

    Kiba518
  • Entity Framework(EF)数据查询

    EF,有一个容器管理着里面所有附着在其上的对象。它们通过一种叫Object Tracking的机制来跟踪对象的变化,以便于在用户需要的时候把这些变化持久化到数据...

    javascript.shop
  • (四十七)c#Winform自定义控件-树表格(treeGrid)

    GitHub:https://github.com/kwwwvagaa/NetWinformControl

    冰封一夏
  • c#钩子学习笔记(一)

    钩子(Hook),是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定窗口的某种消息,而且所监视的窗口可以是其他进程所创建的。当消息到达...

    冰封一夏
  • 剑指offer之面试题2:实现Singleton模式

    在上述代码中,Singleton1的静态属性Instance中,只有在instance为null的时候才创建一个实例以避免重复创建。

    Vincent-yuan

扫码关注云+社区

领取腾讯云代金券