Roslyn入門(二)-C#語義分析
先決條件
簡介
今天,Visual Basic和C#編譯器是黑盒子:輸入文字然後輸出位元組,編譯管道的中間階段沒有透明性。使用.NET編譯器平臺(以前稱為“Roslyn”),工具和開發人員可以利用編譯器使用的完全相同的資料結構和演算法來分析和理解程式碼。
本篇文章,我們將探索Symbol和BindingAPI。透過語法API來檢視解析器,語法樹,用於推理和構造它們的實用程式。
理解編譯和符號
這個語法API能讓你看程式的結構。但是,通常您需要有關程式語義或含義的更豐富資訊。雖然鬆散的程式碼片段可以單獨進行語法分析,但孤立的提出諸如“這個變數的型別是什麼”之類的問題並不是很有意義。型別名稱的含義可能取決於程式集引用,名稱空間匯入或其他程式碼檔案。這就是Compilation類的用武之地。
編譯類似於編譯器看到的單個專案,表示編譯Visual Basic或C#程式所需的所有內容,例如程式集引用,編譯器選項和要編譯的原始檔集。 透過此上下文,您可以推斷出程式碼的含義。 編譯允許您查詢符號 - 名稱和其他表示式引用的型別,名稱空間,成員和變數等實體。 將名稱和表示式與符號(Symbols)相關聯的過程稱為Binding。
與SyntaxTree一樣,Compilation是一個具有特定語言派生類的抽象類。建立Compilation例項時,必須在CSharpCompilation(或VisualBasicCompilation)類上呼叫工廠方法。
演示-建立編譯
- 引入Nuget
Microsoft.CodeAnalysis.CSharp
Microsoft.CodeAnalysis.CSharp.Workspaces
- 上節提到的演示Main程式碼
class Program
{
static void Main(string[] args)
{
SyntaxTree tree = CSharpSyntaxTree.ParseText(
@"using System;
using System.Collections.Generic;
using System.Text;
namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(""Hello, World!"");
}
}
}");
var root = (CompilationUnitSyntax)tree.GetRoot();
}
}
- 接下來,在Main方法的末尾建立CSharpCompilation物件
var compilation = CSharpCompilation.Create("HelloWorld")
.AddReferences(
MetadataReference.CreateFromFile(
typeof(object).Assembly.Location))
.AddSyntaxTrees(tree);
- 設定斷點,啟動除錯,在compilation處檢視提示。
語義模型SemanticModel
一旦你有了編譯,你可以要求它為該編譯中包含的任何SyntaxTree提供SemanticModel。你可以查詢SemanticModel來回答諸如“這個位置的範圍是什麼名稱?”,“從這種方法可以獲得哪些成員?” ,“在這個文字塊中使用了哪些變數?”和“這個名字/表達是指什麼?”之類的問題。
示例-繫結名稱
此示例顯示如何為HelloWorld SyntaxTree獲取SemanticModel物件。獲得SemanticModel後,第一個using指令中的名稱繫結為System名稱空間的符號。
- 將下段程式碼放到Main的末尾。
var model = compilation.GetSemanticModel(tree);
var nameInfo = model.GetSymbolInfo(root.Usings[0].Name);
var systemSymbol = (INamespaceSymbol)nameInfo.Symbol;
*追加以下程式碼,列舉System名稱空間的子名稱空間並將其名稱列印到控制檯:
foreach (var ns in systemSymbol.GetNamespaceMembers())
{
Console.WriteLine(ns.Name);
}
- Debug進入除錯,檢視每個節點的值。Console輸出結果如下:
Buffers
Collections
ComponentModel
Configuration
Diagnostics
Globalization
IO
Numerics
Reflection
Resources
Runtime
Security
StubHelpers
Text
Threading
示例–繫結表示式
前面的示例顯示瞭如何繫結name去查詢Symbol。但是,在C#程式中還有其他不是Name的表示式可以繫結。此示例顯示繫結如何與其他表示式型別一起使用 - 在本例中為簡單的字串文字。
var helloWorldString = root.DescendantNodes()
.OfType<LiteralExpressionSyntax>()
.First();
var literalInfo = model.GetTypeInfo(helloWorldString);
var stringTypeSymbol = (INamedTypeSymbol)literalInfo.Type;
Console.Clear();
foreach (var name in (from method in stringTypeSymbol.GetMembers()
.OfType<IMethodSymbol>()
where method.ReturnType.Equals(stringTypeSymbol) &&
method.DeclaredAccessibility ==
Accessibility.Public
select method.Name).Distinct())
{
Console.WriteLine(name);
}
- 執行Debug,檢視相關節點的值,Console輸出結果如下
Intern
IsInterned
Create
Copy
ToString
Normalize
Concat
Format
Insert
Join
PadLeft
PadRight
Remove
Replace
Substring
ToLower
ToLowerInvariant
ToUpper
ToUpperInvariant
Trim
TrimStart
TrimEnd
總結
本篇文章演示了語義分析,透過兩個示例分別演示繫結Name查詢Symbol和繫結表示式
我們可以獲得以下幾個知識點:
獲取語法樹的根節點:(CompilationUnitSyntax)tree.GetRoot()
用於建立CSharpCompilation物件:CSharpCompilation.Create(“HelloWorld”).AddReferences( MetadataReference.CreateFromFile( typeof(object).Assembly.Location))
.AddSyntaxTrees(tree)
用於獲取模型:compilation.GetSemanticModel(tree);
獲取Name的Symbol資訊: model.GetSymbolInfo(root.Usings[0].Name);
可獲取具體名稱空間名:(INamespaceSymbol)nameInfo.Symbol;
獲取當前名稱空間下所有成員:systemSymbol.GetNamespaceMembers()
獲取文字表示式:root.DescendantNodes().OfType()
獲取Type資訊 model.GetTypeInfo(helloWorldString)
獲取具體的Type型別 (INamedTypeSymbol)literalInfo.Type;
獲取返回值是string,公開型別的方法:
from method in stringTypeSymbol.GetMembers().OfType()
where method.ReturnType.Equals(stringTypeSymbol) &&
method.DeclaredAccessibility ==
Accessibility.Public
select
原始碼
參考連結
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4662/viewspace-2816002/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 逆向入門分析實戰(二)
- 微軟開源 C# 編譯器 Roslyn微軟C#編譯ROS
- Python資料分析入門(二)Python
- C#快速入門教程(12)—— if語句結構C#
- R語言入門與資料分析R語言
- C#程式語言及.NET 平臺快速入門指南C#
- C#快速入門教程(13)—— switch語句結構C#
- C# WCF入門C#
- C#快速入門C#
- hive從入門到放棄(二)——DDL資料定義Hive
- C#語法——反射,架構師的入門基礎。C#反射架構
- C#語言入門詳解(劉鐵錳)---泛型C#泛型
- C#快速入門教程(14)—— 迴圈語句結構C#
- Gradle入門系列(二)——groovy高階語法Gradle
- 小豬的C語言快速入門系列(二)C語言
- gRPC(二)入門:Protobuf入門RPC
- ButterKnife 從入門到精通 - 原始碼級分析(二)原始碼
- 潛在語義分析
- 《R語言入門與資料分析》——向量索引R語言索引
- C#入門程式碼C#
- SpringBoot入門(二):日誌及自定義屬性Spring Boot
- mySql入門-(二)MySql
- 願碼(ChainDesk.CN):Go語言入門指南(二)AIGo
- lua學習之入門(二)----基礎語法1
- c#二維陣列定義宣告C#陣列
- C#快速入門教程(23)—— using語句和IDisposable介面C#
- C#語言————第二章 C#語言快速熱身C#
- c#入門-while迴圈C#While
- C# 12 Blazor入門教程C#Blazor
- MongoDB for C#基礎入門MongoDBC#
- C# 範型入門 1C#
- c# orm轉貼入門C#ORM
- 《C#入門與提高》 (轉)C#
- VUE_入門講義Vue
- Storm入門指南第二章 入門ORM
- Flutter入門篇(二)Flutter
- Go快速入門(二)Go
- spring入門(二)Spring