source generator 是编译前期生成 .cs 文件的工具,不修改已有类、不运行时逻辑、不替代反射或 di;常见失效原因是未正确引用生成器项目或未启用 emitcompilergeneratedfiles。

Source Generator 不是编译时“魔法”,它不能修改已有类行为、不能生成运行时逻辑、也不能替代反射或 DI;它只在 C# 编译前期(semantic model 可用后)生成 .cs 文件并参与后续编译——理解这点,才能避开 90% 的误用和报错。
为什么 ISourceGenerator 实现后没生效?
最常见原因是未正确引用生成器项目,或未启用生成器支持。SDK 风格项目默认不自动包含 generator 引用,且需要显式启用 CompilerGenerated 特性感知。
- 确保 consuming 项目(即使用生成器的项目)的
.csproj中包含:<itemgroup><projectreference include="..\MyGenerator\MyGenerator.csproj" outputitemtype="Analyzer" referenceoutputassembly="false"></projectreference></itemgroup>
- 必须在 consuming 项目的
.csproj中添加:<propertygroup><emitcompilergeneratedfiles>true</emitcompilergeneratedfiles><compilergeneratedfilesoutputpath>obj/Generated</compilergeneratedfilesoutputpath></propertygroup>
否则即使生成成功,你也看不到输出文件,调试无从下手 - VS 中需关闭“仅我的代码”并在调试器中手动加载生成的
*.g.cs文件(路径见CompilerGeneratedFilesOutputPath),否则断点不会命中
如何安全读取用户代码中的 [Attribute] 并生成对应类?
不能直接 new Attribute 实例——Source Generator 运行时没有运行环境,所有 attribute 数据必须通过 SyntaxReceiver 或 ISyntaxContextReceiver 提前捕获语法节点,再用 semanticModel.GetSymbolInfo() 获取语义符号。
- 推荐用
ISyntaxContextReceiver(.NET 6+):在Execute前就完成语法筛选,避免每次遍历全 AST - 匹配
[MyAutoNotify]时,要检查attributeData.AttributeClass?.ToDisplayString()而非字符串比较,防止别名或 using 冲突 - 生成类名时务必调用
INamedTypeSymbol.ContainingNamespace.ToDisplayString()+name拼接,否则跨命名空间会生成重复类名 - 不要尝试生成
partial class的另一部分去注入字段——C# 编译器要求所有 partial 定义必须在同一编译单元可见,而 generator 输出是后置加入的,会导致 CS0260 错误
IncrementalGenerator 比传统 ISourceGenerator 快在哪?
核心差异在于增量计算:IncrementalGenerator 用 Provider 链描述数据流,编译器可跳过未变更输入对应的生成步骤;传统方式每次全量执行 Execute,哪怕只改了一个字符。
- 必须用
context.RegisterPostInitializationOutput注册初始化输出(如 global usings) - 关键链路示例:
var candidates = context.SyntaxProvider .CreateSyntaxProvider((s, _) => IsCandidate(s), TransformSyntax); var models = candidates.Collect(); context.RegisterSourceOutput(models, GenerateFromModels);
其中IsCandidate必须极轻量(只看语法树节点类型/Kind),重逻辑必须放到TransformSyntax或后续GenerateFromModels - 若在
TransformSyntax中调用semanticModel.GetDeclaredSymbol(),必须用context.Compilation当前实例,不能缓存旧 compilation —— 否则增量模式下会拿到过期符号 - VS 中启用
dotnet build-server shutdown后重试,可排除 IDE 缓存干扰;真实提速需配合大解决方案(>50 个项目)才明显
真正难的不是写对第一个 SourceGenerator,而是当它被用于生成序列化注册、DTO 映射或路由表时,如何让错误信息指向用户代码而非 generator 内部——这要求你在 ReportDiagnostic 里精确绑定 Location.Create() 到原始语法节点,而不是随便打个 Location.None。











