Appearance
第 12 章:增量、调试、测试与下一步
本章目标
- 用主案例重新理解“为什么它是增量生成器”
- 建立一套固定的查看、调试、测试顺序
- 知道学完主案例后最先会遇到哪些扩展链路
- 知道下一步该去哪个文档、看哪个 sample
先看现象
学到这里,最常见的两个状态是:
- 状态 1:主案例大致看懂了,但不知道以后出问题该先查哪里
- 状态 2:已经能接受
sample/01,但继续看别的 sample 时,突然冒出了GeneratorDriver、AdditionalTextsProvider、DiagnosticDescriptor、SourceText.From(...)这些新对象
这一章的任务不是把这些扩展对象全部讲成百科,而是把主案例收口,并且给你一条稳定的继续阅读路线。
再看代码
1. 什么叫“增量”
主案例叫增量生成器,不是因为它“少生成一点”,而是因为它把工作拆成多个步骤,并尽量只在输入变化时重算受影响的部分。
在 sample/01 里,你至少已经见过这几个阶段:
- 固定输出
- 特性筛选
- 类与属性信息提取
- 最终输出
这就是零基础阶段最该建立的增量直觉:
- 先拆步骤
- 再让每一步只关心自己的输入和输出
2. 出问题时先查哪里
建议固定用这套顺序:
- 先看目标类上有没有正确特性
- 再看生成器是否按 Analyzer 方式接入
- 再看
obj/.../generated有没有生成文件 - 再看中间模型是否拿到了正确数据
- 最后看输出方法是否把数据拼成了正确代码
最常看的生成结果目录:
sample/01-tostring-generator/ToStringGenerator.Sample/obj/Debug/net8.0/generated
3. 测试工程到底在做什么
主案例测试最值得先看懂的是 4 步:
- 把字符串源码变成语法树
- 把语法树和引用组装成编译对象
- 手动运行生成器
- 检查诊断和生成结果
你第一次阅读时,优先看这几个 API:
CSharpSyntaxTree.ParseText(...)CSharpCompilation.Create(...)MetadataReference.CreateFromFile(...)RunGeneratorsAndUpdateCompilation(...)
它们的详细解释已经拆到:
4. 学完主案例后最先会碰到的扩展能力
继续看其他 sample 时,你几乎一定会遇到这四类问题:
- 我想看“每个生成器到底生成了什么”
- 我想知道“哪一步重新跑了”
- 我想读取
.graphql、.json这类附加文件和配置 - 我想主动报诊断,或者改用
CreateSyntaxProvider(...)
这些内容不再塞在本章里长篇展开,而是拆成了 4 份扩展专题:
如何验证
按这个顺序做:
- 打开
TestHelpers.cs,确认它是如何创建编译和驱动的 - 打开
ToStringGeneratorTests.cs,找一个测试确认它在断言什么 - 运行:
powershell
dotnet test .\sample\01-tostring-generator\ToStringGenerator.Tests\ToStringGenerator.Tests.csproj- 再去
obj/generated看实际生成文件 - 最后从下面 4 份扩展专题里任选一份继续深入
常见误解
- 误解 1:第 12 章必须把所有高级对象一次讲完
- 不是。本章更重要的职责是收口主案例,并把扩展入口指清楚
- 误解 2:增量只是性能优化词,对阅读主流程没帮助
- 不是。它决定了你为什么要把生成器拆成多段
- 误解 3:学完主案例就结束了
- 不是。真正的掌握来自把相同思路迁移到下一个 sample
本章新名词
CompilationCSharpCompilationGeneratorDriverGeneratorDriverRunResultAdditionalTextsProviderAnalyzerConfigOptionsProviderDiagnosticDescriptorSourceTextCreateSyntaxProvider(...)
本章小结
到这里,你已经具备两个关键能力:
- 能把主案例完整解释清楚
- 知道自己继续深入时,应该去哪里查对象、排错和看扩展样例
里程碑 4:你已经准备好进入后续练习
你现在应该具备的能力
- 能从头解释主案例的生成链路
- 能区分固定输出、目标识别、语义提取和动态输出
- 能通过运行示例、查看生成文件和运行测试三种方式验证结果
- 知道自己下一步应该去做哪个练习项目
自检问题
- 如果让你重做一个最小生成器,你会先从哪一步开始?
- 如果生成结果不对,你会先看
obj、代码管道还是测试? - 如果你要做
sample/02或sample/03,哪些步骤和本案例是共通的?
成功标准自检清单
完成本教程后,你应该能做到下面 7 件事:
- 说清楚什么是“编译时生成代码”
- 独立创建一个简单的源生成器项目
- 理解语法和语义的区别
- 能读取类和属性信息
- 能生成一个基本方法或
ToString() - 知道如何调试和测试生成器
- 知道自己下一步该看哪个 sample 和哪个扩展文档
下一步学习建议
建议顺序:
- 回到
sample/01-tostring-generator/ToStringGenerator/ToStringGenerator.cs再通读一遍 - 对照 术语与 API 手册 查不熟悉的对象
- 继续看 4 份扩展专题中的你当前卡点对应那一份
- 再进入 练习项目与下一步学习
- 先从下面 3 个项目开始练
下一步先练哪 3 个 sample
1. sample/02-builder-generator
先看:
sample/02-builder-generator/BuilderGenerator.Sample/Examples/Product.cssample/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.cssample/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.cssample/04-di-registration-generator/DiRegistrationGenerator/DiRegistrationGenerator.cs
关键片段:
csharp
[Injectable(ServiceLifetime.Singleton)]
public class EmailService : IEmailService
{
}为什么第三个练它:
- 它开始要求你聚合多个目标一起生成统一输出
- 会引入特性参数读取和接口分析
- 这是从“单目标生成”过渡到“项目级汇总生成”的关键一步
如果你把这 3 个 sample 都读通,再回到 练习项目与下一步学习 看 sample/05-09,节奏会更稳。
上一章 | 返回主教程目录 | 进入练习项目与下一步学习