[006] 了解 Roslyn 编译器
維基百科對編譯器的解釋是:編譯器是一種程序,它將某種編程語言編寫的源代碼(原始語言)轉(zhuǎn)換成另一種編程語言(目標(biāo)語言)。編譯是從源代碼(通常為高階語言)到能直接被計(jì)算機(jī)或虛擬機(jī)執(zhí)行的目標(biāo)代碼(通常為低階語言或機(jī)器語言)的翻譯過程。
在 .NET 平臺(tái)中,在執(zhí)行模型的不同階段有兩個(gè)不同的編譯器:一個(gè)叫 Roslyn 編譯器,負(fù)責(zé)把 C# 和 VB 代碼編譯為程序集;另一個(gè)叫 RyuJIT 編譯器,負(fù)責(zé)把程序集中的 IL(中間語言) 代碼編譯為機(jī)器碼。
本文先介紹 Roslyn 編譯器。我們不必深入研究它的工作原理,但要了解它的工作機(jī)制,要知道它可以用來做什么事情。
最初 C# 語言的編譯器是用 C++ 編寫的,后來微軟推出了一個(gè)新的用 C# 自身編寫的編譯器:Roslyn,它屬于自舉編譯器。
所謂自舉編譯器就是指,某種編程語言的編譯器就是用該語言自身來編寫的。自舉編譯器的每個(gè)版本都是用該版本之前的版本來編譯的,但它的第一個(gè)版本必須由其它語言編寫的編譯器來編譯,比如 Roslyn 的第一個(gè)版本是由 C++ 編寫的編譯器來編譯的。很多編程語言發(fā)展成熟后都會(huì)用該語言本身來編寫自己的編譯器,比如 C# 和 Go 語言。
在 .NET 平臺(tái),Roslyn 編譯器負(fù)責(zé)將 C# 和 VB 代碼編譯為程序集。
大多數(shù)現(xiàn)有的傳統(tǒng)編譯器都是“黑盒”模式,它們將源代碼轉(zhuǎn)換成可執(zhí)行文件或庫文件,中間發(fā)生了什么我們無法知道。與之不同的是,Roslyn 允許你通過 API 訪問代碼編譯過程中的每個(gè)階段。
它的工作機(jī)制是管道式的,整個(gè)工作管道包含四個(gè)階段,每個(gè)階段都是一個(gè)獨(dú)立的模塊,每個(gè)模塊都提供了相應(yīng)的 API。集成開發(fā)環(huán)境(IDE)可以利用這些 API 提供方便的工具以提高開發(fā)效率,如代碼高亮、智能提示、重構(gòu)工具、性能分析工具等。此外,通過 Roslyn,開發(fā)者可以在自己的程序中使用編譯器,將編譯器作為一種服務(wù)來使用。
下圖描繪了 Roslyn 工作管道的各個(gè)階段和各階段對應(yīng)的 API,以及各 API 可為 IDE 提供的對應(yīng)功能:
來源:bit.ly/3AKnWybParser(解析)階段,根據(jù)語言語法對源代碼進(jìn)行解析,將源代碼轉(zhuǎn)換為層次化的標(biāo)記集合,形成語法樹。語法樹 API 用于在源代碼編輯器中格式化、著色和代碼大綱。
Declaration(聲明)階段,分析所有引用和導(dǎo)入的元數(shù)據(jù),形成層次化的符號(hào)表。在編輯器和對象瀏覽器中的 Navigation To 特性使用這個(gè) API。
Bind(綁定)階段,對標(biāo)記集合和符號(hào)表進(jìn)行匹配。編輯器中的 Find All References、Rename、Quick Info 和 Extract Method 等特性使用這個(gè) API。
Emit(生成)階段,生成 IL 托管模塊,將一個(gè)或多個(gè) IL 托管模塊和嵌入資源合并成程序集。編輯器中的 Edit and Continue 利用這個(gè)特性完成一次新的編譯。
Roslyn 是少數(shù)幾個(gè)讓你有機(jī)會(huì)觀察所有編譯階段和中間結(jié)果的編譯器之一,它提供的這些 API 可以為語言服務(wù)實(shí)現(xiàn)豐富的功能。例如,代碼高亮使用語法樹,對象瀏覽器使用分層符號(hào)表。
下面我們來做一個(gè)簡單的示例,利用 Roslyn 提供的 API 來動(dòng)態(tài)生成代碼。
創(chuàng)建一個(gè)控制臺(tái)應(yīng)用程序 ConsoleApp,編輯 Program.cs 的代碼如下:
using System;namespace?ConsoleApp {partial?class?Program{static?void?Main(string[] args){HelloFrom("Generated Code");Console.ReadKey();}static?partial?void?HelloFrom(string name);} }再創(chuàng)建一個(gè) .NET Standard 類庫,取名 MyGenerator,并添加兩個(gè) NuGet 包,項(xiàng)目文件內(nèi)容如下:
<Project?Sdk="Microsoft.NET.Sdk"><PropertyGroup><TargetFramework>netstandard2.0</TargetFramework></PropertyGroup><ItemGroup><PackageReference?Include="Microsoft.CodeAnalysis.Analyzers"?Version="3.3.2" /><PackageReference?Include="Microsoft.CodeAnalysis.CSharp"?Version="3.10.0" /></ItemGroup></Project>然后在 MyGenerator 項(xiàng)目中添加一個(gè) Generator.cs 文件,代碼如下:
using Microsoft.CodeAnalysis;namespace?MyGenerator {[Generator]public?class?Generator : ISourceGenerator{public?void?Initialize(GeneratorInitializationContext context){}public?void?Execute(GeneratorExecutionContext context){// find the main methodvar mainMethod = context.Compilation.GetEntryPoint(context.CancellationToken);// build up the source codestring source = $@" using System;namespace {mainMethod.ContainingNamespace.Name} {{public static partial class {mainMethod.ContainingType.Name}{{static partial void HelloFrom(string name){{Console.WriteLine($""Generator says: Hi from '{{name}}'"");}}}} }} ";// add the source code to the compilationcontext.AddSource("generatedSource", source);}} }這里的 source 是我們的動(dòng)態(tài)組裝的代碼,在實(shí)際應(yīng)用中還可以從數(shù)據(jù)庫或文本中讀取代碼片段。
最后在 ConsoleApp 項(xiàng)目中引用 MyGenerator 類庫,并參照如下代碼設(shè)置 OutputItemType 和 ReferenceOutputAssembly 屬性:
<ItemGroup><ProjectReference?Include="..\MyGenerator\MyGenerator.csproj"OutputItemType="Analyzer"ReferenceOutputAssembly="false" /></ItemGroup>運(yùn)行 ConsoleApp,可以看到控制臺(tái)輸出如下內(nèi)容:
Roslyn 的功能非常強(qiáng)大,這個(gè)示例只是演示了 Roslyn 的一個(gè)非常簡單的功能和用途。
Roslyn 不只是一個(gè)編譯器,還是一個(gè)現(xiàn)成的框架,它使得在 .NET 平臺(tái)上創(chuàng)建自己的語言服務(wù)變得更加容易。你可以使用 Roslyn 編譯器的 API 在 .NET 平臺(tái)上開發(fā)一個(gè)完整的應(yīng)用程序,甚至創(chuàng)建你自己的 IDE、編寫你自己的編譯器、解釋器或分析器來編譯和運(yùn)行你自己的編程語言。
加入我們,一起踏上.NET大牛成長之路↓
總結(jié)
以上是生活随笔為你收集整理的[006] 了解 Roslyn 编译器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C# 对接微信支付时生成符合 RFC33
- 下一篇: 共享内存 Actor并发模型到底哪个快