Skip to content

语法树

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 分钟

深入理解性能优化:

  • 红绿树架构详解
  • 性能考虑因素
  • 减少遍历次数的技巧
  • 缓存策略和最佳实践
  • 避免常见性能陷阱

🔗 相关资源

API 文档

学习资源


下一步

基于 MIT 许可发布