能否在单个项目中实现C#源代码生成器的组合使用?
单个项目中组合使用C#源代码生成器的可行性
可以在单个项目中实现多轮源代码生成,让后续生成器基于前序生成器的输出代码工作,无需拆分项目。核心是利用编译器的增量生成机制和生成器执行顺序控制,具体实现要点如下:
控制生成器执行顺序
默认编译器会并行执行所有生成器,要让SecondSourceGenerator依赖FirstSourceGenerator的输出,需通过[Generator]特性的ExecutionOrder参数指定执行优先级:[Generator(ExecutionOrder = -1)] // 数值越小越先执行 public class FirstSourceGenerator : IIncrementalGenerator { // 生成逻辑 } [Generator(ExecutionOrder = 0)] public class SecondSourceGenerator : IIncrementalGenerator { // 基于FirstSourceGenerator的输出生成代码 }开启多轮生成与符号分析
在项目文件(.csproj)中添加配置,让编译器在每轮生成后处理符号并触发后续生成:<PropertyGroup> <!-- 启用增量生成模式 --> <EnableIncrementalGeneratorExecution>true</EnableIncrementalGeneratorExecution> <GeneratorExecutionMode>Incremental</GeneratorExecutionMode> <!-- 可选:输出生成的代码以便调试 --> <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> </PropertyGroup>基于符号操作生成代码
在SecondSourceGenerator中,通过增量生成的SyntaxProvider匹配前序生成的代码节点,再通过编译上下文获取已验证的符号:public void Initialize(IncrementalGeneratorInitializationContext context) { // 匹配FirstSourceGenerator生成的带有特定标记的类 var generatedClasses = context.SyntaxProvider.CreateSyntaxProvider( predicate: (node, _) => node is ClassDeclarationSyntax cls && cls.AttributeLists.Any( al => al.Attributes.Any(a => a.Name.ToString() == "GeneratedByFirstGenerator")), transform: (ctx, _) => ctx.SemanticModel.GetDeclaredSymbol(ctx.Node as ClassDeclarationSyntax) ); // 基于获取到的符号生成新代码 context.RegisterSourceOutput(generatedClasses, (spc, symbol) => { // 使用symbol的属性、方法等信息生成代码 spc.AddSource($"{symbol.Name}_Generated.cs", $"public static class {symbol.Name}Extensions {{ /* 生成代码 */ }}"); }); }关键注意事项
- 给前序生成的代码添加唯一标识(如自定义属性),方便后续生成器精准匹配。
- 实现正确的增量缓存逻辑,避免重复生成,提升编译效率。
- 开启
EmitCompilerGeneratedFiles后,可在项目的obj/Debug/netX/GeneratedFiles目录下查看每轮生成的代码,验证流程是否符合预期。
这种单项目方案既保留了拆分项目时的优势——能操作编译器验证后的符号、每轮生成后自动做语法检查,又无需维护多个项目结构。
内容的提问来源于stack exchange,提问作者user1576055
相关产品推荐
相关产品推荐

