前言
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>
ReferenceOutputAssembly
:如果設定為false,則不包括引用專案的輸出作為此專案的引用,但仍可確保在此專案之前生成其他專案。 預設為 true。OutputItemType
:可選項,將目標要輸出的型別,預設為空,如果引用值設定為true(預設值),那麼目標輸出將為編譯器的引用。
Reference
https://github.com/hueifeng/BlogSample/tree/master/src/SourceGeneratorDemo