C# 源生成器(Source Generator)入門
C#9 引入了一個強大的機制:源生成器(Source Generator)。通過創建源生成器,我們可以簡化大量重復編寫的代碼,或是減少反射來獲得更強的性能以及AOT支持
本文將介紹如何從零開始創建一個最簡單的源生成器
創建生成器項目
創建一個SourceGeneratorDemo.Generator項目,目標框架需要設置為.NET Standard 2.0
這大概由于Visual Studio尚未遷移到.NET Core,Framework最高支持.NET Standard 2.0的項目
添加Microsoft.CodeAnalysis.Analyzers、Microsoft.CodeAnalysis.CSharp的nuget引用。
添加EnForceExtendedAnalyzerRules屬性,強制禁用一些分析器不適用的API,否則IDE會有警告
(此屬性的具體作用可以看lindexi大佬的這篇文章)
<PropertyGroup>
...
<EnForceExtendedAnalyzerRules>true</EnForceExtendedAnalyzerRules>
</PropertyGroup>
最終csproj文件如下:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>preview</LangVersion>
<Nullable>enable</Nullable>
<EnForceExtendedAnalyzerRules>true</EnForceExtendedAnalyzerRules>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="4.14.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" />
</ItemGroup>
</Project>
編寫源生成器
創建SampleGenerator.cs,添加[Generator]特性并實現IIncrementalGenerator接口
舊的源生成器
ISourceGenerator在每次代碼有更改時都會掃描整個語法樹,開銷很大。而新的增量生成器IIncrementalGenerator通過管道等方式遴選需要掃描的代碼,大大減少生成開銷。因此這里我們選擇
IIncrementalGenerator進行實現
[Generator]
public class SampleGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
//在這里編寫生成器邏輯
}
}
我們希望生成的代碼如下,這里就放在一個常量中:
private const string HelloWorld =
"""
//加上此行以防止編譯器進行不必要的代碼分析,避免出現警告
//<auto-generated>
using System;
namespace SourceGeneratorDemo.Generator;
public class HelloWorld
{
public static void SayHello()
{
Console.WriteLine("Hello, World!");
}
}
""";
現在來編寫Initialize方法,這是源生成器的核心部分:
public void Initialize(IncrementalGeneratorInitializationContext context)
{
//在編譯時生成源代碼
//HelloWorld.g.cs就是生成代碼的文件名稱
context.RegisterPostInitializationOutput(ctx => ctx.AddSource("HelloWorld.g.cs",SourceText.From(HelloWorld, Encoding.UTF8)));
}
所有代碼如下:
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
namespace SourceGeneratorDemo.Generator;
[Generator]
public class SampleGenerator : IIncrementalGenerator
{
private const string HelloWorld =
"""
//加上此行以防止編譯器進行不必要的代碼分析,避免出現警告
//<auto-generated>
using System;
namespace SourceGeneratorDemo.Generator;
public class HelloWorld
{
public static void SayHello()
{
Console.WriteLine("Hello, World!");
}
}
""";
public void Initialize(IncrementalGeneratorInitializationContext context)
{
//在編譯時生成源代碼
context.RegisterPostInitializationOutput(ctx =>
ctx.AddSource("HelloWorld.g.cs",SourceText.From(HelloWorld, Encoding.UTF8)));
}
}
在其他項目中引用源生成器
創建一個控制臺項目SourceGeneratorDemo.Console,設為我們的啟動項目
添加項目引用后,我們還要在csproj文件里做出以下調整:
<ProjectReference Include="..\SourceGeneratorDemo.Generator\SourceGeneratorDemo.Generator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"/>
OutputItemType屬性說明源生成器是作為一個分析器(Analyzer)引入項目;
ReferenceOutputAssembly阻止源生成器的程序集復制到輸出文件夾,因為其只在編譯時起作用
重新生成解決方案后,以Rider為例,我們可以在項目的依賴項>.NET 9.0(取決于當前項目的.NET版本)>SourceGeneratorDemo.Generator.SampleGenerator中找到剛剛生成的HelloWorld.g.cs,其內容與剛剛常量中的代碼完全一致

若使用Visual Studio 2022,在項目的依賴項>分析器>SourceGeneratorDemo.Generator>SourceGeneratorDemo.Generator.SampleGenerator中也可以找到相同文件

現在我們就可以在Program.cs引用生成的內容了:
using SourceGeneratorDemo.Generator;
HelloWorld.SayHello();
編譯成功即可看到輸出
%E5%85%A5%E9%97%A8/03.png)
源碼
https://github.com/zxbmmmmmmmmm/SourceGeneratorDemo

浙公網安備 33010602011771號