前言
Source Generators
顧名思義代碼生成器,可進行建立編譯時代碼,也就是所謂的
編譯時元程式設計
,這可讓一些運作時映射的代碼改為編譯時,同樣也加快了速度,我們可避免那種昂貴的開銷,這是有價值的。
實作ISourceGenerator
內建
ISourceGenerator
接口,實作接口用于代碼生成政策,它的生命周期由編譯器控制,它可以在編譯時建立時建立并且添加到編譯中的代碼,它為我們提供了編譯時元程式設計,進而使我們對C#代碼或者非C#源檔案進行内部的檢查。
[Generator]
class CustomGenerator: ISourceGenerator
{
public void Initialize(GeneratorInitializationContext context)
{
throw new NotImplementedException();
}
public void Execute(GeneratorExecutionContext context)
{
throw new NotImplementedException();
}
}
public void Execute(GeneratorExecutionContext context)
{
var compilation = context.Compilation;
var simpleCustomInterfaceSymbol = compilation.GetTypeByMetadataName("SourceGeneratorDemo.ICustom");
const string code = @"
using System;
namespace SourceGeneratorDemo
{
public partial class Program{
public static void Display(){
Console.WriteLine(""Hello World!"");
}
}
}";
if (!(context.SyntaxReceiver is CustomSyntaxReceiver receiver))
return;
//文法樹可參考Roslyn Docs
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(code);
//context.AddSource("a.class", code);
context.AddSource("a.class", SourceText.From(text: code, encoding: Encoding.UTF8));
//https://github.com/gfoidl-Tests/SourceGeneratorTest
{
StringBuilder sb = new();
sb.Append(@"using System;
using System.Runtime.CompilerServices;
#nullable enable
[CompilerGenerated]
public static class ExportDumper
{
public static void Dump()
{");
foreach (BaseTypeDeclarationSyntax tds in receiver.Syntaxes)
{
sb.Append($@"
Console.WriteLine(""type: {GetType(tds)}\tname: {tds.Identifier}\tfile: {Path.GetFileName(tds.SyntaxTree.FilePath)}"");");
}
sb.AppendLine(@"
}
}");
SourceText sourceText = SourceText.From(sb.ToString(), Encoding.UTF8);
context.AddSource("DumpExports.generated", sourceText);
static string GetType(BaseTypeDeclarationSyntax tds) => tds switch
{
ClassDeclarationSyntax => "class",
RecordDeclarationSyntax => "record",
StructDeclarationSyntax => "struct",
_ => "-"
};
}
}
context.AddSource
字元串形式的源碼添加到編譯中,
SourceText
原文本抽象類,
SourceText.From
靜态方法,Code指定要建立的源碼内容,Encoding設定儲存的編碼格式,預設為UTF8,
context.SyntaxReceiver
可以擷取在初始化期間注冊的
ISyntaxReceiver
,擷取建立的執行個體
實作ISyntaxReceiver
ISyntaxReceiver
接口作為一個文法收集器的定義,可實作該接口,通過對該接口的實作,可過濾生成器所需
public class CustomSyntaxReceiver : ISyntaxReceiver
{
public List<BaseTypeDeclarationSyntax> Syntaxes { get; } = new();
/// <summary>
/// 通路文法樹
/// </summary>
/// <param name="syntaxNode"></param>
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
{
if (syntaxNode is BaseTypeDeclarationSyntax cds)
{
Syntaxes.Add(cds);
}
}
}
可以再此處進行過濾,如通過
ClassDeclarationSyntax
過濾
Class
類,當然也可以改為
BaseTypeDeclarationSyntax
,或者也可以使用
InterfaceDeclarationSyntax
添加接口類等等
調試
對于Source Generator可以通過添加
Debugger.Launch()
的形式進行對編譯時的生成器進行調試,可以通過它很便捷的一步步調試代碼.
public void Initialize(GeneratorInitializationContext context)
{
Debugger.Launch();
......
}
Library&Properties
<ItemGroup>
<ProjectReference Include="..\ClassLibrary1\ClassLibrary1.csproj"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false" />
</ItemGroup>
-
:如果設定為false,則不包括引用項目的輸出作為此項目的引用,但仍可確定在此項目之前生成其他項目。 預設為 true。ReferenceOutputAssembly
-
:可選項,将目标要輸出的類型,預設為空,如果引用值設定為true(預設值),那麼目标輸出将為編譯器的引用。OutputItemType
Reference
https://github.com/hueifeng/BlogSample/tree/master/src/SourceGeneratorDemo