Skip to content

第 11 章:完整 ToString 实现

本章目标

  • 从上到下串起主案例的完整输出流程
  • 看懂 GenerateToStringCode(...)BuildToStringImplementation(...) 的职责分工
  • 形成对完整生成器实现的整体把握

本章在主线里的位置

  • 对应 Level 3:完整 ToString() 实现
  • 这一章会把固定输出、目标识别、信息提取和复杂情况处理全部串回同一条链路

先看现象

到这一章,你已经零散掌握了这些片段:

  • 固定输出怎么做
  • 目标类怎么找到
  • 语法和语义怎么分工
  • 类信息和属性信息怎么提取
  • 为什么真实场景里还要处理 null、泛型和嵌套类

这一章要做的,就是把这些零散步骤串成一条完整链路。

再看代码

主案例最终的输出逻辑,核心在两个方法里:

  • GenerateToStringCode(...)
  • BuildToStringImplementation(...)

GenerateToStringCode(...) 负责什么

你可以把它理解成“拼整个文件壳子”的方法。它主要负责:

  • 文件头和 #nullable enable
  • 自动生成注释
  • 命名空间
  • 外层嵌套类结构
  • 目标类声明
  • ToString() 方法外壳

也就是说,它处理的是“这个生成文件整体长什么样”。

BuildToStringImplementation(...) 负责什么

这个方法更像“只处理 return 表达式里的正文”。它主要负责:

  • 先拼类名
  • 再遍历属性
  • 决定每个属性要如何插值
  • 决定 null 时输出什么

也就是说,它处理的是“最终返回的字符串内容长什么样”。

为什么要拆成两个方法

如果所有逻辑都挤在一个方法里,很快会出现:

  • 文件结构逻辑和属性格式化逻辑混在一起
  • 读起来难
  • 改起来更难
  • 出问题时不好定位

所以主案例把它拆成两层:

  • 外层:生成完整 C# 文件
  • 内层:生成返回字符串

这是一个很值得你以后复用的设计习惯。

把整条链路再说一遍

你现在可以把主案例从输入到输出总结成 4 步:

  1. 生成固定特性定义
  2. 按特性筛到目标类并提取中间模型
  3. 根据中间模型生成完整 .g.cs
  4. 通过测试和运行结果验证生成是否正确

如果你能完整说出这 4 步,说明你已经真正看懂了这个案例。

如何验证

按下面顺序做:

  1. 先读 GenerateToStringCode(...),给它每一段逻辑写出一句用途说明
  2. 再读 BuildToStringImplementation(...),标出哪些分支在处理不同类型的属性
  3. 打开 sample/01-tostring-generator/ToStringGenerator.Tests/ToStringGeneratorTests.cs
  4. 找到一个测试用例,对照它的 expectedCode 看最终生成结果长什么样

动手练习

  1. 解释为什么 GenerateToStringCode(...)BuildToStringImplementation(...) 要拆成两个方法
  2. 找出没有属性时最终返回的字符串长什么样
  3. 用自己的话描述:完整 ToString 生成器从输入到输出经历了哪 4 步

常见误解

  • 误解 1:把所有代码都放在一个生成方法里更直接
    • 对短例子可以,但真实生成器很快会失控
  • 误解 2:完整实现只是把前面内容简单拼起来
    • 不是,真正难的是让这些步骤之间的边界保持清晰
  • 误解 3:只要能输出字符串,结构如何都无所谓
    • 结构设计直接决定后续可读性、可维护性和可测试性

本章新名词

  • StringBuilder
  • override
  • 插值字符串
  • 自动生成注释

本章小结

到这里,你已经能把固定输出、目标识别、语义提取和代码生成串成一条完整链路了。

里程碑 3:你已经能生成完整功能

你现在应该具备的能力

  • 能看懂主案例完整生成链路
  • 能解释简单动态生成和完整 ToString() 的差异
  • 能指出 null、泛型、嵌套类为什么会增加复杂度
  • 能通过测试和生成文件验证结果
  • 有能力继续进入后续练习项目

自检问题

  1. 为什么完整 ToString() 不能只依赖类名?
  2. 为什么要先转成中间模型?
  3. 如果你要做 Builder 生成器,哪些步骤可以复用?

下一章我们会把这些理解收口:解释“增量”到底是什么意思,以及你应该怎么调试、测试并继续往后学。

上一章 | 返回主教程目录 | 下一章:增量、调试、测试与下一步

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