编辑
2025-09-22
C#
00

目录

🎯 问题分析:为什么要深入了解执行顺序?
常见痛点
💡 解决方案:揭秘编译器执行流程
🚀 Roslyn编译器中的Generator执行阶段
🔍 底层执行顺序详解
基本组件与职责
🛠 代码实战:从简单到复杂
📌 最简单的执行顺序示例
🎯 完整的执行顺序监控Generator
🎯 核心总结

你是否遇到过这样的困扰:明明写好了Source Generator,编译时却不按预期执行?或者多个Generator之间相互干扰,输出结果混乱?这些问题的根源往往在于对Source Generator底层执行机制的理解不足。

本文将带你深入C# Source Generator的内核,从编译器视角揭秘代码生成的完整流程。通过实战案例和关键节点分析,让你彻底掌握Generator的执行顺序和最佳实践,轻松避开常见陷阱。

🎯 问题分析:为什么要深入了解执行顺序?

常见痛点

在实际开发中,开发者经常遇到这些问题:

  • 生成时机混乱:不知道Generator何时执行,导致依赖关系处理不当
  • 多Generator冲突:多个Generator同时工作时出现意外结果
  • 性能问题:Generator执行效率低下,拖慢编译速度
  • 调试困难:无法准确定位Generator执行过程中的问题

这些问题的核心在于缺乏对Source Generator执行机制的深度理解。

💡 解决方案:揭秘编译器执行流程

🚀 Roslyn编译器中的Generator执行阶段

C# Source Generator并非独立运行,而是深度集成在Roslyn编译器的管道中:

text
源码解析 → 语法分析 → 语义分析 → Generator执行 → 代码合并 → IL生成

关键执行节点:

  1. 初始化阶段:Generator注册和依赖收集
  2. 分析阶段:语法树遍历和数据提取
  3. 生成阶段:代码生成和写入
  4. 合并阶段:生成代码与原始代码合并

🔍 底层执行顺序详解

Generator的生命周期包含以下关键步骤:

  1. Initialize()调用 - Generator初始化
  2. RegisterForSyntaxNotifications() - 语法通知注册
  3. CreateSyntaxReceiver() - 语法接收器创建
  4. Execute()调用 - 核心执行逻辑

基本组件与职责

  • 编译器(Roslyn):负责解析源码、构建语法树(SyntaxTree)、语义模型(SemanticModel)、执行分析器/生成器并最终生成编译结果。
  • SyntaxTree / SyntaxNode:源代码的语法表示,生成器经常基于语法接收器(SyntaxReceiver)或增量 API 访问这些节点。
  • SemanticModel:在需要类型信息时由生成器使用,用来解析符号(如 INamedTypeSymbol、IFieldSymbol 等)。
  • ISourceGenerator / IIncrementalGenerator:生成器的两种实现接口:
    • ISourceGenerator:传统、较简单的接口(Initialize + Execute),适合小型生成器。
    • IIncrementalGenerator:增量式 API(推荐),能更高效地响应输入变更并缓存中间结果,减少不必要的重新生成。
  • Analyzer Host:在编译期间注册并执行生成器,负责将生成的源文件注入到编译管线中(作为额外的 SyntaxTree)。

🛠 代码实战:从简单到复杂

📌 最简单的执行顺序示例

让我们从一个基础示例开始,观察Generator的执行顺序:

C#
using System; using System.Diagnostics; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace SimpleOrderGenerator { [Generator] public class SimpleOrderGenerator : ISourceGenerator { public void Initialize(GeneratorInitializationContext context) { // 第一步:初始化阶段 Debug.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] Initialize 调用"); context.RegisterForSyntaxNotifications(() => new SimpleSyntaxReceiver()); } public void Execute(GeneratorExecutionContext context) { // 第二步:执行阶段 Debug.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] Execute 开始"); var syntaxReceiver = context.SyntaxReceiver as SimpleSyntaxReceiver; // 生成简单的代码 string source = @" using System; namespace Generated { public static class TimeStamp { public static string GeneratedAt = """ + DateTime.Now + @"""; } }"; context.AddSource("TimeStamp.g.cs", source); Debug.WriteLine($"[{DateTime.Now:HH:mm:ss.fff}] Execute 完成"); } } public class SimpleSyntaxReceiver : ISyntaxReceiver { public void OnVisitSyntaxNode(SyntaxNode syntaxNode) { // 语法节点访问时调用 if (syntaxNode is ClassDeclarationSyntax) { Debug.WriteLine($"发现类: {((ClassDeclarationSyntax)syntaxNode).Identifier}"); } } } }

🎯 完整的执行顺序监控Generator

为了深入理解执行过程,我们来创建一个完整的监控Generator:

C#
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace SimpleOrderGenerator { [Generator] public class SimpleOrderGenerator : ISourceGenerator { private static readonly List<string> ExecutionLog = new(); public void Initialize(GeneratorInitializationContext context) { LogExecution("Initialize - 开始"); // 注册语法通知 context.RegisterForSyntaxNotifications(() => { LogExecution("SyntaxReceiver - 创建"); return new OrderTrackingSyntaxReceiver(); }); // 注册后续操作 context.RegisterForPostInitialization(ctx => { LogExecution("PostInitialization - 执行"); GenerateExecutionReport(ctx); }); LogExecution("Initialize - 完成"); } public void Execute(GeneratorExecutionContext context) { LogExecution("Execute - 开始"); var receiver = context.SyntaxReceiver as OrderTrackingSyntaxReceiver; LogExecution($"Execute - 发现 {receiver?.ClassCount} 个类"); // 生成主要代码 GenerateMainCode(context); LogExecution("Execute - 完成"); } private void GenerateMainCode(GeneratorExecutionContext context) { var sourceCode = $@" using System; using System.Collections.Generic; namespace ExecutionOrder {{ public static class GeneratorExecutionInfo {{ public static List<string> ExecutionSteps = new List<string> {{ {string.Join(",\n ", ExecutionLog.Select(log => $@"""{log}"""))} }}; public static void PrintExecutionOrder() {{ Console.WriteLine(""=== Generator 执行顺序 ===""); for (int i = 0; i < ExecutionSteps.Count; i++) {{ Console.WriteLine($""{{i + 1}}. {{ExecutionSteps[i]}}""); }} }} }} }}"; context.AddSource("ExecutionOrder.g.cs", sourceCode); } private void GenerateExecutionReport(GeneratorPostInitializationContext context) { var reportCode = @" using System; namespace ExecutionOrder { [System.AttributeUsage(System.AttributeTargets.Class)] public class GeneratedInfoAttribute : System.Attribute { public string GeneratedAt { get; } public GeneratedInfoAttribute(string generatedAt) { GeneratedAt = generatedAt; } } }"; context.AddSource("GeneratedInfoAttribute.g.cs", reportCode); } private static void LogExecution(string message) { var timestamp = DateTime.Now.ToString("HH:mm:ss.fff"); var logEntry = $"[{timestamp}] {message}"; ExecutionLog.Add(logEntry); Debug.WriteLine(logEntry); } } } public class OrderTrackingSyntaxReceiver : ISyntaxReceiver { public int ClassCount { get; private set; } public void OnVisitSyntaxNode(SyntaxNode syntaxNode) { if (syntaxNode is ClassDeclarationSyntax classDecl) { ClassCount++; Debug.WriteLine($"[Receiver] 访问类: {classDecl.Identifier.ValueText}"); } } }

image.png

项目配置文件

XML
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>netstandard2.0</TargetFramework> <LangVersion>latest</LangVersion> <Nullable>enable</Nullable> <IncludeBuildOutput>false</IncludeBuildOutput> <GeneratePackageOnBuild>false</GeneratePackageOnBuild> <IsRoslynComponent>true</IsRoslynComponent> <EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all" /> <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.1" PrivateAssets="all" /> </ItemGroup> </Project>

🎯 核心总结

通过深入剖析C# Source Generator的底层机制,我们掌握了三个关键要点:

  1. 执行流程把控:Generator按照Initialize → SyntaxReceiver → Execute的固定顺序执行,理解这个流程是避免时序问题的基础
  2. 依赖关系管理:多个Generator之间无法保证执行顺序,应在单个Generator内部管理代码依赖关系,避免跨Generator依赖
  3. 性能优化策略:通过缓存机制、增量检查和合理的执行时机选择,可显著提升Generator的执行效率

掌握这些底层逻辑,不仅能让你写出更稳定的Source Generator,还能在遇到复杂场景时游刃有余。你在使用Source Generator时遇到过哪些执行顺序相关的问题?欢迎在评论区分享你的实战经验!

觉得这篇技术深度解析对你有帮助?请转发给更多C#同行,让大家一起提升Generator开发水平! 🚀

本文作者:技术老小子

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!