C# 源生成器(Source Generator)入门

[复制链接]
发表于 2025-8-26 03:39:21 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
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大佬的这篇文章)
  1. <PropertyGroup>
  2.         ...
  3.     <EnForceExtendedAnalyzerRules>true</EnForceExtendedAnalyzerRules>
  4. </PropertyGroup>
复制代码
最终csproj文件如下:
  1. <Project Sdk="Microsoft.NET.Sdk">
  2.     <PropertyGroup>
  3.         <TargetFramework>netstandard2.0</TargetFramework>
  4.         <LangVersion>preview</LangVersion>
  5.         <Nullable>enable</Nullable>
  6.         <EnForceExtendedAnalyzerRules>true</EnForceExtendedAnalyzerRules>
  7.     </PropertyGroup>
  8.     <ItemGroup>
  9.       <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="4.14.0">
  10.         <PrivateAssets>all</PrivateAssets>
  11.         <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
  12.       </PackageReference>
  13.       <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" />
  14.     </ItemGroup>
  15. </Project>
复制代码
编写源生成器

创建SampleGenerator.cs,添加[Generator]特性并实现IIncrementalGenerator接口
旧的源生成器ISourceGenerator在每次代码有更改时都会扫描整个语法树,开销很大。而新的增量生成器IIncrementalGenerator通过管道等方式遴选需要扫描的代码,大大镌汰生成开销。
因此这里我们选择IIncrementalGenerator进行实现
  1. [Generator]
  2. public class SampleGenerator : IIncrementalGenerator
  3. {
  4.         public void Initialize(IncrementalGeneratorInitializationContext context)
  5.     {
  6.         //在这里编写生成器逻辑
  7.     }
  8. }
复制代码
我们希望生成的代码如下,这里就放在一个常量中:
  1. private const string HelloWorld =
  2.     """
  3.     //加上此行以防止编译器进行不必要的代码分析,避免出现警告
  4.     //<auto-generated>
  5.     using System;
  6.     namespace SourceGeneratorDemo.Generator;
  7.     public class HelloWorld
  8.     {
  9.         public static void SayHello()
  10.         {
  11.             Console.WriteLine("Hello, World!");
  12.         }
  13.     }
  14.     """;
复制代码
如今来编写Initialize方法,这是源生成器的核心部分:
  1. public void Initialize(IncrementalGeneratorInitializationContext context)
  2. {
  3.     //在编译时生成源代码
  4.     //HelloWorld.g.cs就是生成代码的文件名称
  5.     context.RegisterPostInitializationOutput(ctx => ctx.AddSource("HelloWorld.g.cs",SourceText.From(HelloWorld, Encoding.UTF8)));
  6. }
复制代码
所有代码如下:
  1. using System.Text;
  2. using Microsoft.CodeAnalysis;
  3. using Microsoft.CodeAnalysis.Text;
  4. namespace SourceGeneratorDemo.Generator;
  5. [Generator]
  6. public class SampleGenerator : IIncrementalGenerator
  7. {
  8.     private const string HelloWorld =
  9.         """
  10.         //加上此行以防止编译器进行不必要的代码分析,避免出现警告
  11.         //<auto-generated>
  12.         using System;
  13.         namespace SourceGeneratorDemo.Generator;
  14.         public class HelloWorld
  15.         {
  16.             public static void SayHello()
  17.             {
  18.                 Console.WriteLine("Hello, World!");
  19.             }
  20.         }
  21.         """;
  22.     public void Initialize(IncrementalGeneratorInitializationContext context)
  23.     {
  24.         //在编译时生成源代码
  25.         context.RegisterPostInitializationOutput(ctx =>
  26.             ctx.AddSource("HelloWorld.g.cs",SourceText.From(HelloWorld, Encoding.UTF8)));
  27.     }
  28. }
复制代码
在其他项目中引用源生成器

创建一个控制台项目SourceGeneratorDemo.Console,设为我们的启动项目
添加项目引用后,我们还要在csproj文件里做出以下调整:
  1. [/code]OutputItemType属性说明源生成器是作为一个分析器(Analyzer)引入项目;
  2. ReferenceOutputAssembly阻止源生成器的程序集复制到输出文件夹,因为其只在编译时起作用
  3. 重新生成解决方案后,以Rider为例,我们可以在项目的依赖项>.NET 9.0(取决于当前项目的.NET版本)>SourceGeneratorDemo.Generator.SampleGenerator中找到刚刚生成的HelloWorld.g.cs,其内容与刚刚常量中的代码完全一致
  4. [align=center][img]https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pbWcyMDI0LmNuYmxvZ3MuY29tL2Jsb2cvMzI3NTY1Ny8yMDI1MDgvMzI3NTY1Ny0yMDI1MDgyNTE5MzMyNjgxNi0xMjk2NDUxOTMxLnBuZw==[/img][/align]
  5. 若使用Visual Studio 2022,在项目的依赖项>分析器>SourceGeneratorDemo.Generator>SourceGeneratorDemo.Generator.SampleGenerator中也可以找到相同文件
  6. [align=center][img]https://dis.qidao123.com/imgproxy/aHR0cHM6Ly9pbWcyMDI0LmNuYmxvZ3MuY29tL2Jsb2cvMzI3NTY1Ny8yMDI1MDgvMzI3NTY1Ny0yMDI1MDgyNTE5MzQxMTk0Ni03NzM4MzUwMzMucG5n[/img][/align]
  7. 如今我们就可以在Program.cs引用生成的内容了:
  8. [code]using SourceGeneratorDemo.Generator;
  9. HelloWorld.SayHello();
复制代码
编译成功即可看到输出

源码

https://github.com/zxbmmmmmmmmm/SourceGeneratorDemo
引用

Roslyn 分析器 EnforceExtendedAnalyzerRules 属性的作用
源生成器:根据需要自动生成机械重复代码 | 扑克博客
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表