Appearance
第 11 章:完整 ToString 实现
本章目标
- 从上到下串起主案例的完整输出流程
- 看懂
GenerateToStringCode(...)和BuildToStringImplementation(...)的职责分工 - 形成对完整生成器实现的整体把握
本章在主线里的位置
- 对应 Level 3:完整
ToString()实现 - 这一章会把固定输出、目标识别、信息提取和复杂情况处理全部串回同一条链路
先看现象
到这一章,你已经零散掌握了这些片段:
- 固定输出怎么做
- 目标类怎么找到
- 语法和语义怎么分工
- 类信息和属性信息怎么提取
- 为什么真实场景里还要处理 null、泛型和嵌套类
这一章要做的,就是把这些零散步骤串成一条完整链路。
再看代码
主案例最终的输出逻辑,核心在两个方法里:
GenerateToStringCode(...)BuildToStringImplementation(...)
GenerateToStringCode(...) 负责什么
你可以把它理解成“拼整个文件壳子”的方法。它主要负责:
- 文件头和
#nullable enable - 自动生成注释
- 命名空间
- 外层嵌套类结构
- 目标类声明
ToString()方法外壳
也就是说,它处理的是“这个生成文件整体长什么样”。
BuildToStringImplementation(...) 负责什么
这个方法更像“只处理 return 表达式里的正文”。它主要负责:
- 先拼类名
- 再遍历属性
- 决定每个属性要如何插值
- 决定 null 时输出什么
也就是说,它处理的是“最终返回的字符串内容长什么样”。
为什么要拆成两个方法
如果所有逻辑都挤在一个方法里,很快会出现:
- 文件结构逻辑和属性格式化逻辑混在一起
- 读起来难
- 改起来更难
- 出问题时不好定位
所以主案例把它拆成两层:
- 外层:生成完整 C# 文件
- 内层:生成返回字符串
这是一个很值得你以后复用的设计习惯。
把整条链路再说一遍
你现在可以把主案例从输入到输出总结成 4 步:
- 生成固定特性定义
- 按特性筛到目标类并提取中间模型
- 根据中间模型生成完整
.g.cs - 通过测试和运行结果验证生成是否正确
如果你能完整说出这 4 步,说明你已经真正看懂了这个案例。
如何验证
按下面顺序做:
- 先读
GenerateToStringCode(...),给它每一段逻辑写出一句用途说明 - 再读
BuildToStringImplementation(...),标出哪些分支在处理不同类型的属性 - 打开
sample/01-tostring-generator/ToStringGenerator.Tests/ToStringGeneratorTests.cs - 找到一个测试用例,对照它的
expectedCode看最终生成结果长什么样
动手练习
- 解释为什么
GenerateToStringCode(...)和BuildToStringImplementation(...)要拆成两个方法 - 找出没有属性时最终返回的字符串长什么样
- 用自己的话描述:完整
ToString生成器从输入到输出经历了哪 4 步
常见误解
- 误解 1:把所有代码都放在一个生成方法里更直接
- 对短例子可以,但真实生成器很快会失控
- 误解 2:完整实现只是把前面内容简单拼起来
- 不是,真正难的是让这些步骤之间的边界保持清晰
- 误解 3:只要能输出字符串,结构如何都无所谓
- 结构设计直接决定后续可读性、可维护性和可测试性
本章新名词
StringBuilderoverride- 插值字符串
- 自动生成注释
本章小结
到这里,你已经能把固定输出、目标识别、语义提取和代码生成串成一条完整链路了。
里程碑 3:你已经能生成完整功能
你现在应该具备的能力
- 能看懂主案例完整生成链路
- 能解释简单动态生成和完整
ToString()的差异 - 能指出 null、泛型、嵌套类为什么会增加复杂度
- 能通过测试和生成文件验证结果
- 有能力继续进入后续练习项目
自检问题
- 为什么完整
ToString()不能只依赖类名? - 为什么要先转成中间模型?
- 如果你要做
Builder生成器,哪些步骤可以复用?
下一章我们会把这些理解收口:解释“增量”到底是什么意思,以及你应该怎么调试、测试并继续往后学。