抽丝剥茧!Source Generators原理讲解
前言
前段時間,我們已經(jīng)用Source Generators實(shí)現(xiàn)了好多功能,比如AutoMapper、API最佳實(shí)踐。
你看完那些實(shí)現(xiàn)代碼,是不是還有點(diǎn)云里霧里!
Source Generators到底是怎么做到的?
基礎(chǔ)知識
Source Generators是編譯過程的一部分,它以編譯樹作為輸入,通過分析代碼,動態(tài)生成文件并把它們加入到編譯過程中:
需要注意的是,你只能添加一些東西到代碼,但不能改變現(xiàn)有的代碼。
為了使用Source Generators,你必須創(chuàng)建.Net Standard項(xiàng)目,并引用nuget包Microsoft.CodeAnalysis.CSharp 3.8.0或以上版本。
基本的實(shí)現(xiàn)代碼如下,你必須實(shí)現(xiàn)ISourceGenerator接口,并且用GeneratorAttribute標(biāo)注:
[Generator] public?class?DemoSourceGenerator?:?ISourceGenerator {public?void?Execute(GeneratorExecutionContext?context){throw?new?NotImplementedException();}public?void?Initialize(GeneratorInitializationContext?context){throw?new?NotImplementedException();} }生成器執(zhí)行上下文
主要生成過程通過Execute方法執(zhí)行。
Execute傳遞一個GeneratorExecutionContext實(shí)例,下列是實(shí)例常用的屬性和方法:
AdditionalFiles 獲取當(dāng)前編譯項(xiàng)目文件中的所有AdditionalFiles標(biāo)簽
Compilation 編譯上下文,最重要的對象
AddSource 向編譯器加入代碼,最重要的方法
語法樹
通過GeneratorExecutionContext.Compilation我們可以獲得編譯上下文,有了這個對象,你就可以訪問當(dāng)前編譯項(xiàng)目的整個語法樹(SyntaxTree)。
那什么是語法樹呢?
首先,安裝.NET Compiler Platform SDK。
然后,在VS中打開“視圖”->“其他窗口”->“Syntax Visualizer”。
可以看到,語法樹是一個樹形結(jié)構(gòu),和每一行代碼一一對應(yīng):
語法樹包含三種類型的項(xiàng)——node、token和trivia。
比如public class Class1 { }整體是ClassDeclaration node,下級的Class1則是ClassKeyword token, 而緊跟的空格則是Whitespace trivia。
因此,只要我們遍歷語法樹,即可拿到編譯中的任何代碼。
Demo
現(xiàn)在把上面的綜合起來,我們就可以開發(fā)Source Generators功能了:
public?void?Execute(GeneratorExecutionContext?context) {//獲取第一個附加文件內(nèi)容,用作代碼模板var?template?=?context.AdditionalFiles.First().GetText().ToString();//獲取第一個類名var?className?=?context.Compilation.SyntaxTrees.SelectMany(p?=>?p.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>()).First().Identifier.Text;//?替換文本生成代碼//?你也可以使用模板引擎或者StringBuilder拼接出代碼var?source?=?template.Replace("{Class}",?className);//?向編譯過程添加代碼文件context.AddSource("Demo",?SourceText.From(source,?Encoding.UTF8)); }在待編譯的項(xiàng)目中添加一個附加文件
<ItemGroup><AdditionalFiles?Include="template.txt"??/> </ItemGroup>template.txt的文件內(nèi)容如下:
using?System;namespace?ClassLibrary1 {public?static?class?Demo{public?static?void?SayHello(){Console.WriteLine("Hello {Class}!");}} }編譯后,可以看到生成如下代碼:
結(jié)論
希望我已經(jīng)描述清楚了使用Source Generators的整個過程。
期待你用它開發(fā)出更多更好的功能!
如果你覺得這篇文章對你有所啟發(fā),請關(guān)注我的個人公眾號”My IO“,記住我!
總結(jié)
以上是生活随笔為你收集整理的抽丝剥茧!Source Generators原理讲解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何按 value 对 dictiona
- 下一篇: 没有违反GPL,他们真的给了源码