前言
前段时间,我们已经用Source Generators实现了好多功能,比如AutoMapper、API最佳实践。
你看完那些实现代码,是不是还有点云里雾里!
Source Generators到底是怎么做到的?
基础知识
Source Generators是编译过程的一部分,它以编译树作为输入,通过分析代码,动态生成文件并把它们加入到编译过程中:
需要注意的是,你只能添加一些东西到代码,但不能改变现有的代码。
为了使用Source Generators,你必须创建.Net Standard项目,并引用nuget包Microsoft.CodeAnalysis.CSharp
3.8.0或以上版本。
基本的实现代码如下,你必须实现ISourceGenerator
接口,并且用GeneratorAttribute
标注:
[Generator]
public class DemoSourceGenerator : ISourceGenerator
{public void Execute(GeneratorExecutionContext context){throw new NotImplementedException();}public void Initialize(GeneratorInitializationContext context){throw new NotImplementedException();}
}
生成器执行上下文
主要生成过程通过Execute
方法执行。
Execute
传递一个GeneratorExecutionContext
实例,下列是实例常用的属性和方法:
AdditionalFiles 获取当前编译项目文件中的所有
AdditionalFiles
标签Compilation 编译上下文,最重要的对象
AddSource 向编译器加入代码,最重要的方法
语法树
通过GeneratorExecutionContext.Compilation
我们可以获得编译上下文,有了这个对象,你就可以访问当前编译项目的整个语法树(SyntaxTree)。
那什么是语法树呢?
首先,安装.NET Compiler Platform SDK
。
然后,在VS中打开“视图”->“其他窗口”->“Syntax Visualizer”。
可以看到,语法树是一个树形结构,和每一行代码一一对应:
语法树包含三种类型的项——node、token和trivia。
比如public class Class1 { }
整体是ClassDeclaration node
,下级的Class1
则是ClassKeyword token
, 而紧跟的空格则是Whitespace trivia
。
因此,只要我们遍历语法树,即可拿到编译中的任何代码。
Demo
现在把上面的综合起来,我们就可以开发Source Generators功能了:
public void Execute(GeneratorExecutionContext context)
{//获取第一个附加文件内容,用作代码模板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));
}
在待编译的项目中添加一个附加文件
<ItemGroup><AdditionalFiles Include="template.txt" />
</ItemGroup>
template.txt的文件内容如下:
using System;namespace ClassLibrary1
{public static class Demo{public static void SayHello(){Console.WriteLine("Hello {Class}!");}}
}
编译后,可以看到生成如下代码:
结论
希望我已经描述清楚了使用Source Generators的整个过程。
期待你用它开发出更多更好的功能!
如果你觉得这篇文章对你有所启发,请关注我的个人公众号”My IO“,记住我!