source generator 必须在 .net 5+ sdk 项目中启用,目标框架需为 net5.0 或更高,项目须引用 microsoft.codeanalysis.csharp 并显式引用生成器项目且设 outputitemtype="analyzer" 和 referenceoutputassembly="false";生成器类必须 public、非嵌套、实现 isourcegenerator 并标记 [generator] 特性;execute 中需遍历用户源码 syntaxtree、用 getsemanticmodel 获取语义模型、通过 context.addsource 注入代码,避免依赖未生成类型或硬编码语法树索引。

Source Generator 必须在 .NET 5+ SDK 项目中启用
Source Generator 不是运行时特性,它只在编译阶段介入,所以旧版 .NET Framework 或未启用新 SDK 的项目根本不会触发。你写对了所有代码,但 Generate 方法压根不被调用,大概率是项目没“认出”这是个生成器项目。
实操建议:
- 确认
.csproj文件顶部是<project sdk="Microsoft.NET.Sdk"></project>(不是Microsoft.NET.Sdk.Web等子集 SDK,除非显式导入主 SDK) - 目标框架必须为
<targetframework>net5.0</targetframework>或更高(如net6.0、net8.0),netstandard2.0不支持 - 生成器项目自身需引用
Microsoft.CodeAnalysis.CSharp和Microsoft.CodeAnalysis.Analyzers,且版本与 SDK 内置的 Roslyn 匹配(例如 .NET 6 对应 Roslyn 4.0+,用错版本会静默失败) - 宿主项目(即要使用生成器的项目)需在
.csproj中显式引用生成器项目,并设OutputItemType="Analyzer"和ReferenceOutputAssembly="false"
Generator 类必须实现 ISourceGenerator 接口且标记 [Generator] 特性
很多人写了类、实现了 Initialize 和 Execute,却忘了加 [Generator] —— 这个特性是 Roslyn 找到你的生成器的唯一入口。没有它,整个类会被完全忽略,连编译警告都没有。
实操建议:
- 类必须是
public,且不能是static或嵌套类(Roslyn 不扫描嵌套类型) -
[Generator]必须放在类声明前,且来自Microsoft.CodeAnalysis命名空间(别手误写成自己定义的同名特性) -
Execute方法里不能 throw 异常,否则会导致编译失败;出错应改用context.ReportDiagnostic()发送诊断信息 - 生成的代码必须通过
context.AddSource("FileName.cs", SourceText.From(content, Encoding.UTF8))注入,文件名建议带.g.cs后缀,避免和手写代码冲突
获取语法树或语义模型时,别直接访问 Compilation.SyntaxTrees[0]
常见错误:在 Execute 中硬编码取第一个语法树,结果遇到多文件项目或 Razor/Designer 文件就崩——SyntaxTree 是按源文件来的,而 Compilation 包含所有输入(包括生成的、引用的、甚至 SDK 自带的)。
实操建议:
- 用
context.Compilation.SyntaxTrees遍历,但优先过滤掉非用户源码的树:检查tree.FilePath是否以.cs结尾,且不包含"obj/"、"Generated/"、"Razor/"等路径片段 - 需要语义信息(比如判断某个类型是否继承自
INotifyPropertyChanged)时,必须先调用context.Compilation.GetSemanticModel(tree),不能复用其他树的模型 - 大量遍历节点性能敏感,别在
Execute里做深度递归;考虑用CSharpSyntaxWalker+ShouldVisitXXX控制范围 - 别在生成器里调用
model.GetTypeInfo(node).Type后直接.ToString()比对名称——泛型、可空引用类型、别名都会导致字符串不一致;改用model.GetTypeInfo(node).Type.Equals(...)或IsNamedType("System.Collections.Generic.List`1")
生成的代码不能依赖尚未生成的类型,也不能覆盖已有成员
生成器跑在编译早期,此时用户代码还没编译完,所以你生成的类若引用了另一个正在被生成的类,就会报 CS0246:“找不到类型”。更隐蔽的是:生成的 partial class 若和手写部分有同名方法,C# 编译器会直接报 CS0111,而不是合并。
实操建议:
- 所有生成代码中的类型引用,必须来自已编译的程序集(
context.Compilation能解析到的),不要试图“预测”用户将写的类名 - 用
partial类时,确保手写部分已存在且命名空间、类名完全一致;生成器只负责补全方法,不定义构造函数或字段(除非你明确控制手写部分不定义) - 避免生成
async方法体——除非你手动写完整状态机或引用System.Runtime.CompilerServices,否则生成的代码大概率编译不过 - 调试时,在
Execute开头加Debugger.Launch()并 attach 到dotnet.exe(不是 VS 进程),因为生成器运行在独立的编译器进程中
最麻烦的其实是跨项目引用场景:生成器 A 依赖 B,B 又依赖 C,其中某一个没正确设 ReferenceOutputAssembly="false",整条链就失效——这种问题不会报错,只会“静默跳过”,得靠查看 dotnet build -v:d 日志里有没有 Executing generator 行来确认。











