.NET Compiler Platform SDKgithub
今天,Visual Basic和C#編譯器是黑盒子:輸入文本而後輸出字節,編譯管道的中間階段沒有透明性。使用.NET編譯器平臺(之前稱爲「Roslyn」),工具和開發人員能夠利用編譯器使用的徹底相同的數據結構和算法來分析和理解代碼。數據結構
本篇文章,咱們將探索Symbol和BindingAPI。經過語法API來查看解析器,語法樹,用於推理和構造它們的實用程序。數據結構和算法
這個語法API能讓你看程序的結構。可是,一般您須要有關程序語義或含義的更豐富信息。雖然鬆散的代碼片斷能夠單獨進行語法分析,但孤立的提出諸如「這個變量的類型是什麼」之類的問題並非頗有意義。類型名稱的含義可能取決於程序集引用,命名空間導入或其餘代碼文件。這就是Compilation類的用武之地。工具
編譯相似於編譯器看到的單個項目,表示編譯Visual Basic或C#程序所需的全部內容,例如程序集引用,編譯器選項和要編譯的源文件集。 經過此上下文,您能夠推斷出代碼的含義。 編譯容許您查找符號 - 名稱和其餘表達式引用的類型,名稱空間,成員和變量等實體。 將名稱和表達式與符號(Symbols)相關聯的過程稱爲Binding。spa
與SyntaxTree同樣,Compilation是一個具備特定語言派生類的抽象類。建立Compilation實例時,必須在CSharpCompilation(或VisualBasicCompilation)類上調用工廠方法。調試
Microsoft.CodeAnalysis.CSharp Microsoft.CodeAnalysis.CSharp.Workspaces
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(); } }
var compilation = CSharpCompilation.Create("HelloWorld") .AddReferences( MetadataReference.CreateFromFile( typeof(object).Assembly.Location)) .AddSyntaxTrees(tree);
一旦你有了編譯,你能夠要求它爲該編譯中包含的任何SyntaxTree提供SemanticModel。你能夠查詢SemanticModel來回答諸如「這個位置的範圍是什麼名稱?」,「從這種方法能夠得到哪些成員?」 ,「在這個文本塊中使用了哪些變量?」和「這個名字/表達是指什麼?」之類的問題。code
此示例顯示如何爲HelloWorld SyntaxTree獲取SemanticModel對象。得到SemanticModel後,第一個using指令中的名稱綁定爲System命名空間的符號。orm
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); }
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); }
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<LiteralExpressionSyntax>()
獲取Type信息 model.GetTypeInfo(helloWorldString)
獲取具體的Type類型 (INamedTypeSymbol)literalInfo.Type;
獲取返回值是string,公開類型的方法: from method in stringTypeSymbol.GetMembers().OfType<IMethodSymbol>() where method.ReturnType.Equals(stringTypeSymbol) && method.DeclaredAccessibility == Accessibility.Public select method.Name