附加文件与配置生成 · 第 3 / 9 篇

全局默认值一旦进入生成器,契约边界就变了

项目级默认命名空间、类名和可见性不是便利配置而已,它们会直接改变生成器的输出契约和回退路径。

只要项目开始给生成器提供全局默认值,很多行为就不再是“文件自己决定”了。

这也是第二案例和第一案例最明显的分界之一。

为什么全局默认值不是附属能力

看上去,项目级默认值只是为了少写几次重复配置,比如:

  • 默认命名空间
  • 默认类名
  • 默认可见性

但它真正影响的,是生成器的契约边界。

因为一旦默认值进入系统,生成器面对“不完整输入”时的解释方式就变了。

什么叫“契约边界变了”

在没有全局默认值时,缺了某些配置,生成器可能只能:

  • 回退到文件名约定
  • 回退到 JSON 里的显式值
  • 或者直接报错

但有了全局默认值之后,系统就多了一层正式兜底。

这意味着:

  • 原本会靠文件名约定推导的类名,可能先被全局默认类名吃掉
  • 原本需要逐文件设置的可见性,可能被项目级默认值统一收敛
  • 原本只是“单文件行为”,现在可能会被项目环境整体改写

为什么这会影响回归风险

因为全局默认值一旦变更,受影响的不是某一个输入文件,而是整批没有显式覆盖的文件。

所以第二案例里,最需要测试锁定的往往不是“某个 JSON 能不能读出来”,而是:

  • 文件级元数据是否真的覆盖全局默认值
  • 全局默认值缺失时,是否才会回退到文件名约定
  • 目录约定是不是建立在默认命名空间之上

这类问题一旦漏测,回归半径会比第一案例大很多。

为什么它必须走 CompilerVisibleProperty

项目级默认值的核心价值,不在于“有个地方可以配置”,而在于它是编译阶段可见的一层正式输入。

也就是说,生成器拿到的不是应用运行时配置,而是:

  • PropertyGroup
  • CompilerVisibleProperty
  • build_property.*

这条链路决定了它可以稳定进入 Roslyn 的 GlobalOptions

第二案例里最容易被忽略的一点

很多人以为“全局默认值就是最弱的兜底”。

其实不是。

它虽然通常低于文件级元数据,但经常高于:

  • JSON 里的某些字段
  • 文件名约定
  • “什么都不填”的默认回退

所以它不是可有可无的小补丁,而是一层真正会改变系统行为的配置层。

一句话结论

当全局默认值进入生成器之后,你设计的就不再只是单文件解析规则,而是一套项目级输出契约。

教程导航

继续阅读

当前文章已经挂到教程顺序中,建议按相邻章节继续。

上一篇 文件级元数据不是 JSON 的一部分,它来自 MSBuild 很多人以为命名空间、类名和可见性都应该写在 JSON 里,但第二案例里,这些单文件覆盖项更适合来自 AdditionalFiles 元数据。 下一篇 多文件合并成一个输出类时,真正难的不是读文件 第二案例最难稳住的不是把 JSON 读进来,而是多个输入文件如何合法地并成同一个输出类型。
查看系列目录 查看全部文章

标签

分类