Skip to content

第 12 章:增量、调试、测试与下一步

本章目标

  • 用主案例重新理解“为什么它是增量生成器”
  • 建立一套固定的查看、调试、测试顺序
  • 知道学完主案例后最先会遇到哪些扩展链路
  • 知道下一步该去哪个文档、看哪个 sample

先看现象

学到这里,最常见的两个状态是:

  • 状态 1:主案例大致看懂了,但不知道以后出问题该先查哪里
  • 状态 2:已经能接受 sample/01,但继续看别的 sample 时,突然冒出了 GeneratorDriverAdditionalTextsProviderDiagnosticDescriptorSourceText.From(...) 这些新对象

这一章的任务不是把这些扩展对象全部讲成百科,而是把主案例收口,并且给你一条稳定的继续阅读路线。

再看代码

1. 什么叫“增量”

主案例叫增量生成器,不是因为它“少生成一点”,而是因为它把工作拆成多个步骤,并尽量只在输入变化时重算受影响的部分。

sample/01 里,你至少已经见过这几个阶段:

  • 固定输出
  • 特性筛选
  • 类与属性信息提取
  • 最终输出

这就是零基础阶段最该建立的增量直觉:

  1. 先拆步骤
  2. 再让每一步只关心自己的输入和输出

2. 出问题时先查哪里

建议固定用这套顺序:

  1. 先看目标类上有没有正确特性
  2. 再看生成器是否按 Analyzer 方式接入
  3. 再看 obj/.../generated 有没有生成文件
  4. 再看中间模型是否拿到了正确数据
  5. 最后看输出方法是否把数据拼成了正确代码

最常看的生成结果目录:

  • sample/01-tostring-generator/ToStringGenerator.Sample/obj/Debug/net8.0/generated

3. 测试工程到底在做什么

主案例测试最值得先看懂的是 4 步:

  1. 把字符串源码变成语法树
  2. 把语法树和引用组装成编译对象
  3. 手动运行生成器
  4. 检查诊断和生成结果

你第一次阅读时,优先看这几个 API:

  • CSharpSyntaxTree.ParseText(...)
  • CSharpCompilation.Create(...)
  • MetadataReference.CreateFromFile(...)
  • RunGeneratorsAndUpdateCompilation(...)

它们的详细解释已经拆到:

4. 学完主案例后最先会碰到的扩展能力

继续看其他 sample 时,你几乎一定会遇到这四类问题:

  1. 我想看“每个生成器到底生成了什么”
  2. 我想知道“哪一步重新跑了”
  3. 我想读取 .graphql.json 这类附加文件和配置
  4. 我想主动报诊断,或者改用 CreateSyntaxProvider(...)

这些内容不再塞在本章里长篇展开,而是拆成了 4 份扩展专题:

如何验证

按这个顺序做:

  1. 打开 TestHelpers.cs,确认它是如何创建编译和驱动的
  2. 打开 ToStringGeneratorTests.cs,找一个测试确认它在断言什么
  3. 运行:
powershell
dotnet test .\sample\01-tostring-generator\ToStringGenerator.Tests\ToStringGenerator.Tests.csproj
  1. 再去 obj/generated 看实际生成文件
  2. 最后从下面 4 份扩展专题里任选一份继续深入

常见误解

  • 误解 1:第 12 章必须把所有高级对象一次讲完
    • 不是。本章更重要的职责是收口主案例,并把扩展入口指清楚
  • 误解 2:增量只是性能优化词,对阅读主流程没帮助
    • 不是。它决定了你为什么要把生成器拆成多段
  • 误解 3:学完主案例就结束了
    • 不是。真正的掌握来自把相同思路迁移到下一个 sample

本章新名词

  • Compilation
  • CSharpCompilation
  • GeneratorDriver
  • GeneratorDriverRunResult
  • AdditionalTextsProvider
  • AnalyzerConfigOptionsProvider
  • DiagnosticDescriptor
  • SourceText
  • CreateSyntaxProvider(...)

本章小结

到这里,你已经具备两个关键能力:

  • 能把主案例完整解释清楚
  • 知道自己继续深入时,应该去哪里查对象、排错和看扩展样例

里程碑 4:你已经准备好进入后续练习

你现在应该具备的能力

  • 能从头解释主案例的生成链路
  • 能区分固定输出、目标识别、语义提取和动态输出
  • 能通过运行示例、查看生成文件和运行测试三种方式验证结果
  • 知道自己下一步应该去做哪个练习项目

自检问题

  1. 如果让你重做一个最小生成器,你会先从哪一步开始?
  2. 如果生成结果不对,你会先看 obj、代码管道还是测试?
  3. 如果你要做 sample/02sample/03,哪些步骤和本案例是共通的?

成功标准自检清单

完成本教程后,你应该能做到下面 7 件事:

  1. 说清楚什么是“编译时生成代码”
  2. 独立创建一个简单的源生成器项目
  3. 理解语法和语义的区别
  4. 能读取类和属性信息
  5. 能生成一个基本方法或 ToString()
  6. 知道如何调试和测试生成器
  7. 知道自己下一步该看哪个 sample 和哪个扩展文档

下一步学习建议

建议顺序:

  1. 回到 sample/01-tostring-generator/ToStringGenerator/ToStringGenerator.cs 再通读一遍
  2. 对照 术语与 API 手册 查不熟悉的对象
  3. 继续看 4 份扩展专题中的你当前卡点对应那一份
  4. 再进入 练习项目与下一步学习
  5. 先从下面 3 个项目开始练

下一步先练哪 3 个 sample

1. sample/02-builder-generator

先看:

  • sample/02-builder-generator/BuilderGenerator.Sample/Examples/Product.cs
  • sample/02-builder-generator/BuilderGenerator/BuilderGenerator.cs

关键片段:

csharp
[GenerateBuilder]
public partial class Product
{
    public string Name { get; set; }
    public decimal Price { get; set; }
}

为什么先练它:

  • 触发方式和 sample/01 最接近
  • 但输出结构已经从单个 ToString() 升级成嵌套 Builder
  • 很适合做“从简单输出到结构化输出”的第一步迁移

2. sample/03-enum-extensions-generator

先看:

  • sample/03-enum-extensions-generator/SampleApp/Enums.cs
  • sample/03-enum-extensions-generator/EnumExtensionsGenerator/EnumExtensionsGenerator.cs

关键片段:

csharp
var enumDeclarations = context.SyntaxProvider.CreateSyntaxProvider(
    predicate: IsEnumDeclaration,
    transform: TransformEnum
);

为什么接着练它:

  • 这是你正式从 ForAttributeWithMetadataName(...) 切到 CreateSyntaxProvider(...) 的第一站
  • 输入目标从类切换成枚举,能帮你打破“生成器只能处理类”的习惯
  • 结构不算太重,但足够让你建立新的筛选思路

3. sample/04-di-registration-generator

先看:

  • sample/04-di-registration-generator/SampleApp/Services.cs
  • sample/04-di-registration-generator/DiRegistrationGenerator/DiRegistrationGenerator.cs

关键片段:

csharp
[Injectable(ServiceLifetime.Singleton)]
public class EmailService : IEmailService
{
}

为什么第三个练它:

  • 它开始要求你聚合多个目标一起生成统一输出
  • 会引入特性参数读取和接口分析
  • 这是从“单目标生成”过渡到“项目级汇总生成”的关键一步

如果你把这 3 个 sample 都读通,再回到 练习项目与下一步学习sample/05-09,节奏会更稳。

上一章 | 返回主教程目录 | 进入练习项目与下一步学习

基于当前仓库文档副本构建的 VitePress 站点