使用 Roslyn 进行源码分析时,我们会对很多不同种类的语法节点进行分析。如果能够一次性了解到各种不同种类的语法节点,并明白其含义和结构,那么在源码分析的过程中将会更加得心应手。

本文将介绍 Roslyn 中各种不同的语法节点、每个节点的含义,以及这些节点之间的关系和语法树结构。


 

基本概念

using System;

namespace Walterlv.Demo
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello Walterlv!");
}
}
}

以上是一个非常简单但完整的 .cs 文件。

在 Roslyn 的解析中,这就是一个“编译单元”(Compilation Unit)。编译单元是 Roslyn 语法树的根节点。紧接着的 using System 是 using 指令(Using Directives);随后是命名空间声明(Namespace Declaration),包含子节点类型声明(Class Declaration);类型声明包含子节点方法声明(Method Declaration)。

接下来,我们会介绍 Roslyn 语法树中各种不同种类的节点,以及其含义。

语法节点

语法树

CompilationUnit,是语法树的根节点。

关键字

UsingKeywordNamespaceKeywordPublicKeywordInternalKeywordPrivateKeywordProtectedKeywordStaticKeywordClassKeywordInterfaceKeywordStructKeyword

分别是 C# 的各种关键字:using, namespace, public, internal, private, protected, static, class, interface, struct

InKeywordOutKeywordRefKeywordReturnKeywordConstKeywordDefaultKeyword

分别是 C# 的另一波关键字 inoutrefreturnconstdefault

ByteKeywordCharKeywordIntKeywordLongKeywordBoolKeywordFloatKeywordDoubleKeywordDecimalKeyword

分别是 C# 中的基元类型关键字bytecharintlongboolfloatdoubledecimal。需要注意的是,vardynamic 并不是基元类型关键字,在语法节点中,它是 IdentifierName。

AsyncKeywordAwaitKeyword

分别是 asyncawait 关键字。

TrueKeywordFalseKeyword

分别是 truefalse 关键字。

LockKeywordCheckedKeywordUncheckedKeywordUnsafeKeywordFixedKeyword

分别是 lockcheckeduncheckedunsafefixed 关键字。

符号

DotTokenSemicolonTokenOpenBraceTokenCloseBraceTokenLessThanTokenGreaterThanTokenOpenParenTokenCloseParenToken

分别是 C# 中的各种符号:., ;, {, }, <, >, (, )

空白

EndOfLineTrivia 表示换行,WhitespaceTrivia 表示空格,EndOfFileToken 表示文件的末尾。

通常,这两个语法节点会在另一个节点的里面,作为另一个节点的最后一部分。比如 using Walterlv.Demo; 是一个 UsingDirective,它的最后一个节点 Semicolon 中就会包含换行符 EndOfLineTrivia。

指令

UsingDirectiveusing 指令。一个 using 指令包含一个 UsingKeyword,一个 QualifiedName 和一个 Semicolon(;)。

声明

NamespaceDeclarationClassDeclarationMethodDeclarationPropertyDeclarationFieldDeclarationVariableDeclaration

分别是命名空间、类型、方法、属性、。

其中,属性声明包含一个 AccessorList,即属性访问器列表,访问期列表可以包含 GetAccessorDeclaration(属性 get)、SetAccessorDeclaration(属性 set)的声明。

这些声明通常是嵌套存在的。例如一个常规的文件的第 0、1 级语法节点通常是这样的:

  • CompilationUnit

    • UsingDirective
    • UsingDirective
    • NamespaceDeclaration
    • EndOfFileToken

类型声明是命名空间声明的子节点,类型成员的声明是类型声明的子节点。

名称和标识符

  • QualifiedName

    • 限定名称,可以理解为完整的名称。
    • 例如命名空间 Walterlv.DemoTool 的限定名称就是这个全称 Walterlv.DemoTool;类型 Walterlv.DemoTool.Foo 的限定名称也是这个全程 Walterlv.DemoTool.Foo。
  • IdentifierName
    • 标识名称,当前上下文下的唯一名称。
    • 例如 Walterlv 和 DemoTool 都是 Walterlv.DemoTool 这个命名空间的标识符。
  • IdentifierToken
    • 标识符,具体决定 IdentifierName 的一个字符串。
    • 这其实与 IdentifierName 是一样的意思,但是在语法树上的不同节点。
  • GenericName
    • 泛型名称,即 Foo 这种。

特性

AttributeListAttribute

一个允许添加特性的地方,如果添加了特性,那么可以得到 AttributeList 节点,内部包含了多个 Attribute 子节点。

形参和实参

形参是 parameter,实参是 argument。前者是定义的参数,后者是实际传入的参数。

语法节点中有两种不同的形参和实参,一个是泛型,一个是普通参数。

  • ParameterList

    • 形参列表,出现在方法声明中,即 void Foo(string a, bool b) 中的 (string a, bool b) 部分。
  • Parameter
    • 形参,即以上例子中的 string abool b 部分。
  • ArgumentList
    • 实参列表,出现在方法调用中,即 this.Foo(a, b) 中的 (a, b) 部分。
  • Argument
    • 实参,即以上例子中的 ab 部分。
  • TypeParameterList
    • 泛型形参列表,出现在类型声明或者方法声明中,即 void Foo<T1, T2>(string a) 中的 <T1, T2> 部分。
  • TypeParameter
    • 泛型形参,即以上例子中的 T1T2 部分。
  • TypeArgumentList
    • 泛型实参列表,出现在使用泛型参数的地方,例如 this.Foo<T1, T2>() 中的 <T1, T2> 部分。
  • TypeArgument
    • 泛型实参,即以上例子中的 T1T2 部分。

语句块

  • Block

    • 即用 {} 包裹的语句代码。
    • 当然并不是所有 {} 包裹的都是语句(例如类型声明就不是),里面真正有代码时才是语句。
  • EqualsValueClause
    • 等号子句,例如 = null。我们经常称之为“赋值”语句。

语句

一个语句是指包含分号在内的实际执行的句子。

  • LocalDeclarationStatement

    • 本地变量声明语句,即 var a = 0; 这样的句子;其中,去掉分号的部分即前面我们提到的变量声明 VariableDeclaration。
    • 一个本地变量声明的语句也可以不包含赋值。
  • ExpressionStatement
    • 表达式语句,即 this.Foo(); 这样的一次方法调用。如果去掉分号,剩下的部分是表达式(Expression)。
  • IfStatement
    • if 语句,即一个完整的 if-else if-else
  • ForStatement
    • for 语句。
  • ForEachStatement
    • for 语句。
  • WhileStatement
    • while 语句,即一个完整的 while
  • DoStatement
    • do-while 语句。
  • DefaultStatement
    • default(); 语句。
  • ReturnStatement
    • return 语句。
  • CheckedStatement
    • checked 语句。
  • UncheckedStatement
    • checked 语句。
  • UnsafeStatement
    • unsafe 语句。
  • FixedStatement
    • unsafe 语句。

表达式

  • EqualsExpression

    • 相等判断表达式,即 a == b
  • InvocationExpression
    • 调用表达式,即 Class.Method(xxx)instance.Method(xxx) 这种完整的调用。
  • SimpleMemberAccessExpression
    • 这是 InvocationExpression 的子节点,是方法调用除去参数列表的部分,即 Class.Methodinstance.Method
    • 如果是获取属性(没有参数列表),那么也是这个节点。
  • AwaitExpression
    • await 表达式,即 await this.Foo() 这样的调用。
  • DefaultExpression
    • default() 表达式。
  • TrueLiteralExpression
    • true 表达式。
  • FalseLiteralExpression
    • false 表达式。
  • ParenthesizedLambdaExpression
    • 带括号的 lambda 表达式,例如:
    • () => xxx(a) => xxx(a, b) => xxx(int a, string b) => xxx
    • () => { }(a) => { }(a, b) => { }(int a, string b) => { }
  • SimpleLambdaExpression
    • 不带括号的 lambda 表达式,例如:
    • a => xxxa => { }

基元类型

PredefinedType 是所有基元类型的节点。它的子节点可能是 BoolKeyword、StringKeyword 或其它基元类型的关键字。

C# 内建类型

NullableTypeTupleTypeArrayType

这三个分别是 C# 中语法级别支持的类型,分别是可空类型、元组类型和数组类型。

  • NullableType

    • bool? 这种用于创建 Nullable<bool> 的语法。
  • TupleType
    • (bool, string) 这种用于创建 ValueTuple<bool, string> 的语法。
  • ArrayType
    • [] 这种用于创建数组类型的语法。

Roslyn 语法树中的各种语法节点及每个节点的含义的更多相关文章

  1. JSP编译成Servlet(一)语法树的生成——语法解析

    一般来说,语句按一定规则进行推导后会形成一个语法树,这种树状结构有利于对语句结构层次的描述.同样Jasper对JSP语法解析后也会生成一棵树,这棵树各个节点包含了不同的信息,但对于JSP来说解析后的语 ...

  2. LINQ语法类似于SQL的语法

    LINQ语法类似于SQL的语法如下, Models.BookStoreEntities 是从添加新建项中的数据--->ADO.NET实体数据模型--->从数据库生成--->使用5.0 ...

  3. dom节点及对节点的常用操作方法

    dom节点及对节点的常用操作方法 在说dom节点前,先来看看页面的呈现: dom渲染流程:  1.浏览器解析html源码,然后创建一个DOM树. 在DOM树中,每一个HTML标签都有一个对应的节点(元 ...

  4. 【转】JavaScript获取节点类型、节点名称和节点值

    DOM节点信息包括节点类型(nodeType).节点名称(nodeName)和节点值(nodeValue). 节点类型 DOM节点中,每个节点都拥有不同的类型.W3C规范中常用的 DOM节点类型有以下 ...

  5. SUSE Ceph 增加节点、减少节点、 删除OSD磁盘等操作 - Storage6

    一.测试环境描述 之前我们已快速部署好一套Ceph集群(3节点),现要测试在现有集群中在线方式增加节点 如下表中可以看到增加节点node004具体配置 主机名 Public网络 管理网络 集群网络 说 ...

  6. STL---Codeforces675D Tree Construction(二叉树节点的父亲节点)

    Description During the programming classes Vasya was assigned a difficult problem. However, he doesn ...

  7. 红黑树之 原理和算法详细介绍(阿里面试-treemap使用了红黑树) 红黑树的时间复杂度是O(lgn) 高度<=2log(n+1)1、X节点左旋-将X右边的子节点变成 父节点 2、X节点右旋-将X左边的子节点变成父节点

    红黑树插入删除 具体参考:红黑树原理以及插入.删除算法 附图例说明   (阿里的高德一直追着问) 或者插入的情况参考:红黑树原理以及插入.删除算法 附图例说明 红黑树与AVL树 红黑树 的时间复杂度 ...

  8. Delphi Treeview 用法(概念、属性、添加编辑插入节点、定位节点、拖拽等)

    今天再细研究了一下Treeview的用法,网上虽然总结了很多,但是还是有很多节点没有讲到了,也给使用中遇到很多问题.特地总结一下: 1.概念 Treeview用于显示按照树形结构进行组织的数据.Tre ...

  9. LeetCode-129-求根节点到叶节点数字之和

    求根节点到叶节点数字之和 题目描述:给你一个二叉树的根节点 root ,树中每个节点都存放有一个 0 到 9 之间的数字. 每条从根节点到叶节点的路径都代表一个数字: 例如,从根节点到叶节点的路径 1 ...

随机推荐

  1. 综合一句话Shell破解

    之前我在论坛发过了一句话的破解工具. 所以决定还是在基础上在改改,符合某些人的利用. 上一版只支持HTTPS/ASPX,这次改进后,也算是最后一版. 支持:PHP/HTTP/HTTPSASPX/HTT ...

  2. 浅谈C#泛型的定义、继承、方法和约束

    摘要:本文介绍了如何定义一个C#泛型类,以及实现泛型类的继承.方法和约束. C#泛型参数化了类型,把类型作为参数抽象出来,从而使我们在实际的运用当中能够更好的实现代码的重复利用,同时它提供了更强的类型 ...

  3. 面试笔试总结(一)之 C++基础

    C++ 1.智能指针 内存管理 垃圾回收 指针问题 资源管理(内存就是资源) 可以通过引用计数的机制...实现内存回收,不要让内存泄漏. 涉及到内存的泄露的问题: 当创建一个对象的时候(new)而在对 ...

  4. C5 标准IO库:APUE 笔记

    C5 :标准IO库 在第三章中,所有IO函数都是围绕文件描述符展开,文件描述符用于后续IO操作.由于文件描述符相关的操作是不带缓冲的IO,需要操作者本人指定缓冲区分配.IO长度等,对设备环境要求一定的 ...

  5. 如何理解nRF5芯片外设PPI

    PPI,英文全称Programmable Peripheral Interconnect,是Nordic独有的外设,其设计目的是让CPU处于idle模式下外设与外设之间也能完成相应通信,从而降低系统功 ...

  6. 【Python】更优的字符串格式化方式 -- "format"替代"%s"

    背景 前段时间看了一篇介绍Python的代码技巧的文章,建议格式化字符串时使用"format"代替使用"%",但是没有说明原因.各博客网站介绍相关用法的博客很多 ...

  7. PHP数组合并:[“+”运算符]、[array_merge]、[array_merge_recursive]区别

    1.“+”运算符规则: 当两个数组的键名是数字键名或者字符串键名 $c = $a + $b 在$a后追加($b在$a中不存在的键名)键名和值注意: 1.不覆盖,只是追加不存在的键名和对应的值 2.键名 ...

  8. ElasticSearch + Canal 开发千万级的实时搜索系统【转】

    公司是做社交相关产品的,社交类产品对搜索功能需求要求就比较高,需要根据用户城市.用户ID昵称等进行搜索. 项目原先的搜索接口采用SQL查询的方式实现,数据库表采用了按城市分表的方式.但随着业务的发展, ...

  9. FluentData,一个轻量级开源的.NET ORM数据持久化框架

    FluentData:一种使用Fluent API的新型轻量级ORM模型  FluentData 是微型 ORM(micro-ORM)家族的一名新成员,旨在比大型 ORM(full ORM)更加易用. ...

  10. 设计模式--适配器模式C++实现

    适配器模式C++实现 1定义Adapter 将一个类的接口变成客户端所需要的另外一种借口,从而使远不因为接口不匹配而无法合作的两个雷能够一起工作 又叫变压器模式,包装模式Wrapper 2类图 角色分 ...