使用 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,是語法樹的根節點。
關鍵字
UsingKeyword、NamespaceKeyword、PublicKeyword、InternalKeyword、PrivateKeyword、ProtectedKeyword、StaticKeyword、ClassKeyword、InterfaceKeyword、StructKeyword。
分別是 C# 的各種關鍵字:using
, namespace
, public
, internal
, private
, protected
, static
, class
, interface
, struct
。
InKeyword、OutKeyword、RefKeyword、ReturnKeyword、ConstKeyword、DefaultKeyword。
分別是 C# 的另一波關鍵字 in
、out
、ref
、return
、const
、default
。
ByteKeyword、CharKeyword、IntKeyword、LongKeyword、BoolKeyword、FloatKeyword、DoubleKeyword、DecimalKeyword。
分別是 C# 中的基元類型關鍵字byte
、char
、int
、long
、bool
、float
、double
、decimal
。需要注意的是,var
和 dynamic
並不是基元類型關鍵字,在語法節點中,它是 IdentifierName。
AsyncKeyword、AwaitKeyword。
分別是 async
、await
關鍵字。
TrueKeyword、FalseKeyword。
分別是 true
和 false
關鍵字。
LockKeyword、CheckedKeyword、UncheckedKeyword、UnsafeKeyword、FixedKeyword。
分別是 lock
、checked
、unchecked
、unsafe
、fixed
關鍵字。
符號
DotToken、SemicolonToken、OpenBraceToken、CloseBraceToken、LessThanToken、GreaterThanToken、OpenParenToken、CloseParenToken。
分別是 C# 中的各種符號:.
, ;
, {
, }
, <
, >
, (
, )
。
空白
EndOfLineTrivia 表示換行,WhitespaceTrivia 表示空格,EndOfFileToken 表示文件的末尾。
通常,這兩個語法節點會在另一個節點的裏面,作爲另一個節點的最後一部分。比如 using Walterlv.Demo;
是一個 UsingDirective,它的最後一個節點 Semicolon 中就會包含換行符 EndOfLineTrivia。
指令
UsingDirective 是 using
指令。一個 using
指令包含一個 UsingKeyword,一個 QualifiedName 和一個 Semicolon(;
)。
聲明
NamespaceDeclaration、ClassDeclaration、MethodDeclaration、PropertyDeclaration、FieldDeclaration、VariableDeclaration。
分別是命名空間、類型、方法、屬性、。
其中,屬性聲明包含一個 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 這種。
特性
AttributeList、Attribute。
一個允許添加特性的地方,如果添加了特性,那麼可以得到 AttributeList 節點,內部包含了多個 Attribute 子節點。
形參和實參
形參是 parameter,實參是 argument。前者是定義的參數,後者是實際傳入的參數。
語法節點中有兩種不同的形參和實參,一個是泛型,一個是普通參數。
- ParameterList
- 形參列表,出現在方法聲明中,即
void Foo(string a, bool b)
中的(string a, bool b)
部分。
- 形參列表,出現在方法聲明中,即
- Parameter
- 形參,即以上例子中的
string a
和bool b
部分。
- 形參,即以上例子中的
- ArgumentList
- 實參列表,出現在方法調用中,即
this.Foo(a, b)
中的(a, b)
部分。
- 實參列表,出現在方法調用中,即
- Argument
- 實參,即以上例子中的
a
和b
部分。
- 實參,即以上例子中的
- TypeParameterList
- 泛型形參列表,出現在類型聲明或者方法聲明中,即
void Foo<T1, T2>(string a)
中的<T1, T2>
部分。
- 泛型形參列表,出現在類型聲明或者方法聲明中,即
- TypeParameter
- 泛型形參,即以上例子中的
T1
和T2
部分。
- 泛型形參,即以上例子中的
- TypeArgumentList
- 泛型實參列表,出現在使用泛型參數的地方,例如
this.Foo<T1, T2>()
中的<T1, T2>
部分。
- 泛型實參列表,出現在使用泛型參數的地方,例如
- TypeArgument
- 泛型實參,即以上例子中的
T1
和T2
部分。
- 泛型實參,即以上例子中的
語句塊
- Block
- 即用
{
和}
包裹的語句代碼。 - 當然並不是所有
{
和}
包裹的都是語句(例如類型聲明就不是),裏面真正有代碼時纔是語句。
- 即用
- EqualsValueClause
- 等號子句,例如
= null
。我們經常稱之爲“賦值”語句。
- 等號子句,例如
語句
一個語句是指包含分號在內的實際執行的句子。
- LocalDeclarationStatement
- 本地變量聲明語句,即
var a = 0;
這樣的句子;其中,去掉分號的部分即前面我們提到的變量聲明 VariableDeclaration。 - 一個本地變量聲明的語句也可以不包含賦值。
- 本地變量聲明語句,即
- ExpressionStatement
- 表達式語句,即
this.Foo();
這樣的一次方法調用。如果去掉分號,剩下的部分是表達式(Expression)。
- 表達式語句,即
- IfStatement
- if 語句,即一個完整的
if
-else if
-else
。
- if 語句,即一個完整的
- ForStatement
- for 語句。
- ForEachStatement
- for 語句。
- WhileStatement
- while 語句,即一個完整的
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.Method
或instance.Method
。 - 如果是獲取屬性(沒有參數列表),那麼也是這個節點。
- 這是 InvocationExpression 的子節點,是方法調用除去參數列表的部分,即
- AwaitExpression
- await 表達式,即
await this.Foo()
這樣的調用。
- await 表達式,即
- 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 => xxx
、a => { }
基元類型
PredefinedType 是所有基元類型的節點。它的子節點可能是 BoolKeyword、StringKeyword 或其它基元類型的關鍵字。
C# 內建類型
NullableType、TupleType、ArrayType。
這三個分別是 C# 中語法級別支持的類型,分別是可空類型、元組類型和數組類型。
- NullableType
- 即
bool?
這種用於創建Nullable<bool>
的語法。
- 即
- TupleType
- 即
(bool, string)
這種用於創建ValueTuple<bool, string>
的語法。
- 即
- ArrayType
- 即
[]
這種用於創建數組類型的語法。
- 即