CSharpSyntaxTree.ParseText需显式指定CSharpParseOptions语言版本,语法节点不可变须赋值接收ReplaceNode返回值,生成代码须用NormalizeWhitespace().ToFullString()并用SyntaxFactory构造合法节点。

用 CSharpSyntaxTree.ParseText 读取源码时,别漏掉 ParseOptions
直接 CSharpSyntaxTree.ParseText("class A {}") 看似能跑,但一遇到 async、record、global using 就报错:「unexpected token」或语法树不完整。这是因为默认解析器只支持 C# 6 语法,而新版特性需要显式指定语言版本。
- 必须传入
new CSharpParseOptions(LanguageVersion.CSharp12)(按项目实际版本选) - 如果源码含 UTF-8 BOM 或换行符异常,加
encoding: Encoding.UTF8避免字符解析错位 - 从文件读取时,优先用
CSharpSyntaxTree.ParseText(File.ReadAllText(path), options),别用File.ReadAllBytes+Encoding手动转——容易丢元数据(比如行号映射)
修改语法树后,root.ReplaceNode 不生效?检查是否用了不可变节点
Roslyn 的语法节点全是不可变(immutable)的,ReplaceNode 返回的是新树,原 root 变量没变。常见错误是写了 root.ReplaceNode(old, new) 却没接返回值,后续还拿旧 root 去生成代码,结果什么都没改。
- 必须赋值:
root = root.ReplaceNode(oldNode, newNode) - 批量替换用
root.ReplaceNodes,它接收一个IEnumerable<SyntaxNode>和转换函数,比循环调ReplaceNode安全(避免中间状态污染) - 如果要插入新节点(如加字段),别直接 new
FieldDeclarationSyntax——得用SyntaxFactory.FieldDeclaration系列工厂方法,否则缺少必要的语法令牌(如分号、修饰符顺序)
生成字符串时,root.ToString() 和 root.NormalizeWhitespace().ToFullString() 差在哪
ToString() 只输出原始解析后的文本(缩进乱、缺换行、无空格),根本不能当源码用;ToFullString() 也不行——它保留所有原始空白,但没格式化逻辑。真正可用的是 NormalizeWhitespace() 后再 ToFullString()。
-
NormalizeWhitespace()会补全缺失的空格/换行(比如if(x){}→if (x) { }),还能传参控制缩进风格(indentation: " ") - 若需匹配团队代码风格,建议额外用
Formatter.Format(root, workspace)(需引入Microsoft.CodeAnalysis.Workspaces),但它依赖AdhocWorkspace和文档上下文,轻量脚本里慎用 - 注意:
NormalizeWhitespace不改变语法结构,只是美化;如果节点本身缺SemicolonToken,它不会帮你加——得先用工厂方法构造合法节点
引用 Microsoft.CodeAnalysis.CSharp 后,编译报错“找不到类型或命名空间”
常见于 .NET SDK 项目(Sdk="Microsoft.NET.Sdk"),因为 Roslyn 包默认不参与编译引用传递。即使 NuGet 装了,using Microsoft.CodeAnalysis 仍标红。
- 必须在
.csproj中显式设<IncludeSourceGeneratorAttributes>true</IncludeSourceGeneratorAttributes>(非必需,但防坑) - 更关键的是加
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" PrivateAssets="all" />,且PrivateAssets="all"要保留,否则可能和项目自带的编译器版本冲突 - 如果用的是 .NET 8+,推荐统一用
Microsoft.CodeAnalysis.Common+CSharp组合包,别混用Microsoft.CodeAnalysis(旧全量包,已弃用)
语法树不是字符串拼接,节点关系、令牌位置、语义模型三者咬合很紧。少一个 SyntaxFactory.Token,或者忘了调 WithLeadingTrivia 补注释,生成的代码就可能编译失败——这种细节没报错提示,只能靠调试时看 root.ToFullString() 输出肉眼排查。










