source generators 是编译期静态代码生成技术,必须在 csc.exe 执行期间完成,依赖增量编译机制、正确项目配置(outputitemtype="analyzer")、.g.cs 命名规范及 additionalfiles 外部输入,禁止运行时 api 和磁盘 i/o。

Source Generators 不是“运行时动态生成”,而是编译期静态注入代码。它不能替代 CompileAssemblyFromSource 或 CSharpCodeProvider,也不能在程序启动后生成新类型——所有生成的代码必须在 csc.exe 执行期间完成,并参与后续编译流程。
为什么 Initialize 方法里必须用 IncrementalValueProvider
Roslyn 的增量编译机制会缓存中间结果(如语法树、语义模型),但只对显式声明依赖项的节点做脏检查。如果你在 Execute 中直接遍历 compilation.SyntaxTrees,每次编译都会触发全量重执行,哪怕只改了一个空格。
- 必须用
context.SyntaxProvider.CreateSyntaxProvider过滤目标节点(比如带[AutoSerialize]的类) - 再通过
context.CompilationProvider.SelectMany关联语义模型,避免GetSemanticModelAsync在错误时机调用 - 最终用
pipeline.RegisterSourceOutput输出,而非手写context.AddSource—— 后者绕过增量链,等于放弃性能优势
文件名和注入方式写错会导致编译冲突
生成的代码必须以 .g.cs 结尾(例如 AutoSerializer.g.cs),否则可能和手写 partial class 文件名重复,引发 CS0101(命名空间中已存在该类型)。
-
context.AddSource("MyClass.g.cs", SourceText.From(content, Encoding.UTF8))是唯一合法注入方式 - 不能用
File.WriteAllText写磁盘文件——编译器看不到,也不会参与编译 - 生成内容里不能包含
namespace块以外的顶层语句(如裸using static),否则报 CS8795
目标项目引用 generator 时容易漏掉关键属性
generator 项目必须被识别为 analyzer,否则 Initialize 根本不会被调用。常见错误是只加 ProjectReference,却没设 OutputItemType="Analyzer"。
- 目标项目的
.csproj中必须这样写:<itemgroup><projectreference include="..\MyGenerator\MyGenerator.csproj" outputitemtype="Analyzer" referenceoutputassembly="false"></projectreference></itemgroup>
-
ReferenceOutputAssembly="false"很关键:它阻止 generator 的 DLL 被复制进输出目录,避免运行时加载失败或版本冲突 - generator 项目自身需设
<isanalyzer>true</isanalyzer>,否则 SDK 不会启用 analyzer 模式
生成逻辑里别碰运行时 API
generator 运行在编译器进程内,没有 AppDomain、没有配置文件、没有当前工作目录保证。像 Directory.GetCurrentDirectory() 或 File.ReadAllText("config.json") 在 CI 环境大概率失败。
- 所有输入必须来自语法树或编译器上下文(
GeneratorSyntaxContext、SemanticModel) - 如果真要读外部配置,得用
AdditionalFiles机制,在目标项目中显式声明:<itemgroup><additionalfiles include="serializer.config.json"></additionalfiles></itemgroup>
然后在 generator 中用context.AdditionalFiles获取 - JSON 反序列化要用
System.Text.Json,别用Newtonsoft.Json—— generator 项目不能引用非netstandard2.0兼容的包
最常被忽略的一点:生成的代码一旦注入,就和手写代码完全等价,包括可调试性、IntelliSense 支持、以及命名冲突检查。但 generator 本身没有 IDE 实时反馈,出错只能看编译日志里的 CS8773 或 CS0579,建议在 Execute 开头加 #nullable disable 和最小化字符串拼接,先让基础结构跑通再说。











