Skip to content

符号系统

📚 文档导航

本文档是符号系统系列的主文档,介绍基础概念和快速入门。

📖 文档系列

文档内容难度
符号系统基础基础概念、符号层次、快速入门🟢 入门
符号类型详解INamedTypeSymbol、IMethodSymbol、IPropertySymbol 等🟡 中级
符号操作获取、遍历、查询、比较符号🟡 中级
高级主题继承、接口、特性、显示格式、文档注释🔴 高级
最佳实践性能优化、实战场景、设计模式🟡 中级

什么是符号?

符号(Symbol)表示代码中的实体,如类、方法、属性、字段等。

💡 核心概念

符号是 Roslyn 中表示程序元素的语义信息的对象。每个符号都包含:

  • 名称和完整限定名
  • 类型信息
  • 访问修饰符
  • 成员信息
  • 特性(Attribute)
  • 继承关系

符号的作用

符号提供:

  • 完整的类型信息 - 了解类型的结构和成员
  • 成员信息 - 访问类、方法、属性等的详细信息
  • 访问修饰符 - 检查可访问性
  • 特性信息 - 获取应用的特性
  • 继承关系 - 分析类型层次结构

符号层次结构


快速开始

获取符号的基本方式

csharp
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

public class SymbolBasics
{
    public void GetSymbolExample()
    {
        var code = @"
            namespace MyApp
            {
                public class Person
                {
                    public string Name { get; set; }
                    public int Age { get; set; }
                    
                    public void SayHello()
                    {
                        Console.WriteLine($""Hello, I'm {Name}"");
                    }
                }
            }
        ";
        
        // 创建语法树和编译单元
        var tree = CSharpSyntaxTree.ParseText(code);
        var compilation = CSharpCompilation.Create("MyAssembly")
            .AddReferences(MetadataReference.CreateFromFile(typeof(object).Assembly.Location))
            .AddSyntaxTrees(tree);
        
        var model = compilation.GetSemanticModel(tree);
        var root = tree.GetRoot();
        
        // 获取类符号
        var classDecl = root.DescendantNodes()
            .OfType<ClassDeclarationSyntax>()
            .First();
        
        var classSymbol = model.GetDeclaredSymbol(classDecl) as INamedTypeSymbol;
        
        Console.WriteLine($"类名: {classSymbol.Name}");
        Console.WriteLine($"完整名称: {classSymbol.ToDisplayString()}");
        Console.WriteLine($"命名空间: {classSymbol.ContainingNamespace.ToDisplayString()}");
        Console.WriteLine($"访问级别: {classSymbol.DeclaredAccessibility}");
    }
}

常用符号类型概览

1. INamedTypeSymbol

表示类、结构、接口、枚举、委托。

csharp
var typeSymbol = semanticModel.GetDeclaredSymbol(classDeclaration) 
    as INamedTypeSymbol;

// 基本信息
string name = typeSymbol.Name;
string fullName = typeSymbol.ToDisplayString();
TypeKind kind = typeSymbol.TypeKind; // Class, Struct, Interface, etc.

// 访问修饰符
bool isPublic = typeSymbol.DeclaredAccessibility == Accessibility.Public;

// 修饰符
bool isAbstract = typeSymbol.IsAbstract;
bool isSealed = typeSymbol.IsSealed;
bool isStatic = typeSymbol.IsStatic;

// 继承关系
INamedTypeSymbol baseType = typeSymbol.BaseType;
ImmutableArray<INamedTypeSymbol> interfaces = typeSymbol.Interfaces;

📚 深入学习

详细了解符号类型,请查看 符号类型详解

2. IMethodSymbol

表示方法。

csharp
var methodSymbol = semanticModel.GetDeclaredSymbol(methodDeclaration) 
    as IMethodSymbol;

// 基本信息
string name = methodSymbol.Name;
ITypeSymbol returnType = methodSymbol.ReturnType;
ImmutableArray<IParameterSymbol> parameters = methodSymbol.Parameters;

// 修饰符
bool isAsync = methodSymbol.IsAsync;
bool isStatic = methodSymbol.IsStatic;
bool isVirtual = methodSymbol.IsVirtual;
bool isOverride = methodSymbol.IsOverride;

3. IPropertySymbol

表示属性。

csharp
var propertySymbol = semanticModel.GetDeclaredSymbol(propertyDeclaration) 
    as IPropertySymbol;

// 基本信息
string name = propertySymbol.Name;
ITypeSymbol type = propertySymbol.Type;

// 访问器
IMethodSymbol getter = propertySymbol.GetMethod;
IMethodSymbol setter = propertySymbol.SetMethod;

bool isReadOnly = getter != null && setter == null;
bool isWriteOnly = getter == null && setter != null;

获取符号的方式

从语法节点获取

csharp
// 获取声明的符号
var symbol = semanticModel.GetDeclaredSymbol(node);

// 获取引用的符号
var symbol = semanticModel.GetSymbolInfo(node).Symbol;

// 获取类型信息
var typeInfo = semanticModel.GetTypeInfo(expression);
var type = typeInfo.Type;

📚 深入学习

详细了解符号操作,请查看 符号操作详解

从编译获取

csharp
// 通过完整名称获取类型
var typeSymbol = compilation.GetTypeByMetadataName("System.String");

// 获取全局命名空间
var globalNamespace = compilation.GlobalNamespace;

// 获取程序集符号
var assemblySymbol = compilation.Assembly;

符号比较

使用 SymbolEqualityComparer

csharp
// ✅ 正确:使用 SymbolEqualityComparer
bool areEqual = SymbolEqualityComparer.Default.Equals(symbol1, symbol2);

// ❌ 错误:使用 == 或 Equals
// bool areEqual = symbol1 == symbol2;  // 不要这样做!
// bool areEqual = symbol1.Equals(symbol2);  // 不要这样做!

// 在字典中使用
var symbolMap = new Dictionary<ISymbol, string>(
    SymbolEqualityComparer.Default);

⚠️ 重要

永远使用 SymbolEqualityComparer.Default 来比较符号,不要使用 ==Equals()


实际应用示例

查找所有公共方法

csharp
var publicMethods = typeSymbol.GetMembers()
    .OfType<IMethodSymbol>()
    .Where(m => m.DeclaredAccessibility == Accessibility.Public)
    .Where(m => m.MethodKind == MethodKind.Ordinary); // 排除构造函数等

查找实现特定接口的类

csharp
bool ImplementsInterface(INamedTypeSymbol typeSymbol, string interfaceName)
{
    return typeSymbol.AllInterfaces.Any(i => 
        i.ToDisplayString() == interfaceName);
}

获取泛型类型参数

csharp
if (typeSymbol.IsGenericType)
{
    var typeArgs = typeSymbol.TypeArguments;
    foreach (var arg in typeArgs)
    {
        Console.WriteLine($"类型参数: {arg.Name}");
    }
}

符号工作流程


性能提示

✅ 推荐

csharp
// 使用 SymbolEqualityComparer
var set = new HashSet<ISymbol>(SymbolEqualityComparer.Default);

// 缓存符号查找结果
var typeSymbol = compilation.GetTypeByMetadataName("MyType");

❌ 避免

csharp
// 不要使用字符串比较
if (symbol.Name == "MyClass") // 可能有命名空间冲突

// 不要重复查找
foreach (var item in items)
{
    var type = compilation.GetTypeByMetadataName("MyType"); // 慢!
}

⚠️ 性能提示

符号查找相对昂贵,应该缓存并重用。详细的性能优化技巧请查看 最佳实践


下一步学习

📚 深入学习符号系统

🔗 相关主题

📖 API 参考

💡 实战示例


🔗 外部资源


最后更新: 2025-01-21

基于 MIT 许可发布