语法树
Roslyn 语法树是 C# 和 VB.NET 代码的结构化表示,它将源代码转换为可分析和操作的树形结构。本文档提供语法树的快速入门和常用操作参考。
📋 快速导航
| 章节 | 难度 | 阅读时间 | 链接 |
|---|---|---|---|
| 🚀 快速开始 | 🟢 基础 | 5 分钟 | 查看 |
| 📊 常用操作速查表 | 🟢 基础 | 5 分钟 | 查看 |
| 🔄 遍历方式对比 | 🟡 中级 | 5 分钟 | 查看 |
| 📖 语法树概览 | 🟢 基础 | 10 分钟 | 查看 |
| 📚 深入学习 | - | - | 查看 |
深入学习主题
| 主题 | 难度 | 阅读时间 | 文档链接 |
|---|---|---|---|
| 🎯 基础操作 | 🟢 基础 | 65 分钟 | syntax-tree-basics.md |
| 🔄 遍历方法 | 🟡 中级 | 75 分钟 | syntax-tree-traversal.md |
| ✏️ 修改操作 | 🟡 中级 | 75 分钟 | syntax-tree-modification.md |
| ⚡ 性能优化 | 🔴 高级 | 90 分钟 | syntax-tree-performance.md |
🚀 快速开始 🟢
解析代码
点击查看完整示例
csharp
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
var code = @"
public class Person
{
public string Name { get; set; }
}";
// 解析代码生成语法树
var tree = CSharpSyntaxTree.ParseText(code);
var root = tree.GetRoot();查找节点
csharp
// 查找所有类声明
var classes = root.DescendantNodes()
.OfType<ClassDeclarationSyntax>();
foreach (var cls in classes)
{
Console.WriteLine($"类名: {cls.Identifier.Text}");
}📊 常用操作速查表 🟢
| 操作 | 方法 | 示例 | 场景 |
|---|---|---|---|
| 解析代码 | CSharpSyntaxTree.ParseText() | var tree = CSharpSyntaxTree.ParseText(code); | 将源代码文本转换为语法树 |
| 获取根节点 | GetRoot() | var root = tree.GetRoot(); | 获取语法树的根节点以开始遍历 |
| 查找所有节点 | DescendantNodes() | var nodes = root.DescendantNodes(); | 递归获取所有子孙节点 |
| 过滤特定类型 | OfType<T>() | var classes = root.DescendantNodes().OfType<ClassDeclarationSyntax>(); | 筛选特定类型的语法节点 |
| 查找第一个匹配 | FirstOrDefault() | var cls = root.DescendantNodes().OfType<ClassDeclarationSyntax>().FirstOrDefault(); | 查找第一个符合条件的节点 |
| 条件过滤 | Where() | var publicMethods = methods.Where(m => m.Modifiers.Any(mod => mod.IsKind(SyntaxKind.PublicKeyword))); | 根据条件筛选节点 |
| 替换节点 | ReplaceNode() | var newRoot = root.ReplaceNode(oldNode, newNode); | 创建新树并替换指定节点 |
| 修改标识符 | WithIdentifier() | var newClass = classDecl.WithIdentifier(SyntaxFactory.Identifier("NewName")); | 修改节点的标识符名称 |
| 添加成员 | AddMembers() | var newClass = classDecl.AddMembers(newMethod); | 向类或结构添加新成员 |
| 修改修饰符 | WithModifiers() | var newMethod = method.WithModifiers(newModifiers); | 更改节点的访问修饰符 |
性能提示
- 一次遍历收集多种信息: 使用
SyntaxWalker或单次foreach循环 - 缓存常用结果: 避免重复查询相同的节点
- 使用
ToList(): 如果需要多次访问结果,先转换为列表
🔄 遍历方式对比 🟡
| 方式 | 适用场景 | 性能 | 灵活性 | 代码复杂度 | 使用建议 |
|---|---|---|---|---|---|
| DescendantNodes | 简单查询、过滤节点 | ⚡ 高 | 🔸 低 | 🟢 低 | 适合大多数场景,使用 LINQ 进行简单过滤和查询 |
| SyntaxWalker | 复杂遍历、需要上下文 | 🔸 中 | ⚡ 高 | 🟡 中 | 需要在遍历过程中维护状态或上下文时使用 |
| SyntaxRewriter | 修改节点、代码转换 | 🔸 中 | ⚡ 高 | 🟡 中 | 需要修改语法树时的首选方式 |
| 递归遍历 | 自定义复杂逻辑 | 🔴 低 | ⚡⚡ 最高 | 🔴 高 | 仅在其他方式无法满足需求时使用 |
🎯 快速决策
需要修改语法树?
├─ 是 → 使用 SyntaxRewriter
└─ 否 → 继续
需要维护遍历状态或上下文?
├─ 是 → 使用 SyntaxWalker
└─ 否 → 继续
需要简单查询或过滤?
├─ 是 → 使用 DescendantNodes ✅ (推荐)
└─ 否 → 考虑递归遍历(谨慎使用)避免性能陷阱
- ❌ 不要多次遍历:不要为每种节点类型单独调用
DescendantNodes() - ❌ 不要频繁访问父节点:会创建大量红节点,影响性能
- ❌ 不要创建不必要的节点:只在需要时创建新节点
📖 语法树概览 🟢
什么是语法树?
语法树(Syntax Tree)是源代码的结构化表示,它将代码文本转换为树形结构。Roslyn 的语法树具有以下特点:
1. 完整性 🟢 基础
包含所有源代码信息,包括:
- 代码本身
- 空格和换行
- 注释
- 预处理指令
2. 不可变性 🟡 中级
一旦创建就不能修改,只能创建新的树。这确保了线程安全和可预测性。
3. 红绿树结构 🔴 高级
Roslyn 使用红绿树优化内存和性能。绿节点是不可变的共享节点,红节点包含位置信息。
三种节点类型
1. SyntaxNode(语法节点)
表示声明、语句、表达式等结构化元素。
示例:
ClassDeclarationSyntax- 类声明MethodDeclarationSyntax- 方法声明IfStatementSyntax- if 语句
csharp
// 获取类声明节点
var classDeclaration = root.DescendantNodes()
.OfType<ClassDeclarationSyntax>()
.First();2. SyntaxToken(语法标记)
表示关键字、标识符、运算符等最小语法单元。
示例:
public- 关键字Person- 标识符{- 符号
csharp
// 获取类名标记
var className = classDeclaration.Identifier;
Console.WriteLine(className.Text); // "Person"3. SyntaxTrivia(琐碎内容)
表示空格、注释、预处理指令等非结构化内容。
csharp
// 获取注释
var trivia = token.LeadingTrivia
.Where(t => t.IsKind(SyntaxKind.SingleLineCommentTrivia));语法树结构示例
源代码:
csharp
public class Person
{
public string Name { get; set; }
}对应的树结构:
CompilationUnit
└── ClassDeclaration
├── Modifiers: [public]
├── Keyword: class
├── Identifier: Person
└── Members
└── PropertyDeclaration
├── Modifiers: [public]
├── Type: string
├── Identifier: Name
└── AccessorList
├── GetAccessor
└── SetAccessor📚 深入学习
想要深入了解语法树的各个方面?我们为您准备了四个专题文档:
🎯 基础操作
难度: 🟢 基础 | 阅读时间: 65 分钟
学习语法树的基础知识:
- 语法树的详细概念和特点
- 如何创建和解析语法树
- 基本的查询和导航方法
- 节点类型和属性详解
- 常用的 LINQ 查询模式
🔄 遍历方法
难度: 🟡 中级 | 阅读时间: 75 分钟
掌握各种遍历技术:
- DescendantNodes 详解和最佳实践
- SyntaxWalker 访问者模式
- SyntaxRewriter 修改树的技巧
- 手动遍历和自定义逻辑
- 遍历性能对比和优化
✏️ 修改操作
难度: 🟡 中级 | 阅读时间: 75 分钟
学习如何修改语法树:
- 不可变性原则和修改策略
- 添加、删除、替换节点
- 批量修改和代码转换
- 保留格式和注释
- 常见修改模式和陷阱
⚡ 性能优化
难度: 🔴 高级 | 阅读时间: 90 分钟
深入理解性能优化:
- 红绿树架构详解
- 性能考虑因素
- 减少遍历次数的技巧
- 缓存策略和最佳实践
- 避免常见性能陷阱