Skip to content

符号操作详解

📚 文档导航

本文档详细介绍符号的获取、遍历、查询和比较操作。

📖 文档系列

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

获取符号的方式

获取方式对比

从语法节点获取

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

public class GetSymbolFromSyntax
{
    public void DemonstrateGetSymbol()
    {
        var code = @"
            public class Person
            {
                public string Name { get; set; }
                
                public void SayHello()
                {
                    Console.WriteLine(Name);
                }
            }
        ";
        
        var tree = CSharpSyntaxTree.ParseText(code);
        var compilation = CreateCompilation(tree);
        var model = compilation.GetSemanticModel(tree);
        var root = tree.GetRoot();
        
        // 1. GetDeclaredSymbol - 获取声明的符号
        var classDecl = root.DescendantNodes()
            .OfType<ClassDeclarationSyntax>()
            .First();
        
        var classSymbol = model.GetDeclaredSymbol(classDecl);
        Console.WriteLine($"类符号: {classSymbol.ToDisplayString()}");
        
        // 2. GetSymbolInfo - 获取引用的符号
        var identifier = root.DescendantNodes()
            .OfType<IdentifierNameSyntax>()
            .First(i => i.Identifier.Text == "Name");
        
        var symbolInfo = model.GetSymbolInfo(identifier);
        Console.WriteLine($"引用的符号: {symbolInfo.Symbol?.ToDisplayString()}");
        
        // 3. GetTypeInfo - 获取类型信息
        var expression = root.DescendantNodes()
            .OfType<IdentifierNameSyntax>()
            .First();
        
        var typeInfo = model.GetTypeInfo(expression);
        Console.WriteLine($"类型: {typeInfo.Type?.ToDisplayString()}");
    }
    
    private Compilation CreateCompilation(SyntaxTree tree)
    {
        return CSharpCompilation.Create("temp")
            .AddReferences(
                MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
                MetadataReference.CreateFromFile(typeof(Console).Assembly.Location)
            )
            .AddSyntaxTrees(tree);
    }
}

💡 区别

  • GetDeclaredSymbol: 获取声明的符号(定义)
  • GetSymbolInfo: 获取引用的符号(使用)
  • GetTypeInfo: 获取表达式的类型信息

从编译单元获取

csharp
public class GetSymbolFromCompilation
{
    public void DemonstrateGetSymbol(Compilation compilation)
    {
        // 1. 通过完整名称获取类型
        var stringType = compilation.GetTypeByMetadataName("System.String");
        var listType = compilation.GetTypeByMetadataName("System.Collections.Generic.List`1");
        
        Console.WriteLine($"String 类型: {stringType?.ToDisplayString()}");
        Console.WriteLine($"List<T> 类型: {listType?.ToDisplayString()}");
        
        // 2. 获取特殊类型
        var objectType = compilation.ObjectType;
        var dynamicType = compilation.DynamicType;
        
        Console.WriteLine($"Object 类型: {objectType.ToDisplayString()}");
        Console.WriteLine($"Dynamic 类型: {dynamicType.ToDisplayString()}");
        
        // 3. 获取程序集符号
        var assembly = compilation.Assembly;
        Console.WriteLine($"程序集: {assembly.Name}");
        Console.WriteLine($"程序集版本: {assembly.Identity.Version}");
        
        // 4. 获取全局命名空间
        var globalNamespace = compilation.GlobalNamespace;
        Console.WriteLine($"全局命名空间: {globalNamespace.ToDisplayString()}");
        
        // 5. 遍历所有引用的程序集
        Console.WriteLine("\n引用的程序集:");
        foreach (var reference in compilation.References)
        {
            var assemblySymbol = compilation.GetAssemblyOrModuleSymbol(reference) as IAssemblySymbol;
            if (assemblySymbol != null)
            {
                Console.WriteLine($"  - {assemblySymbol.Name}");
            }
        }
    }
}

符号遍历

深度遍历符号树

csharp
public class SymbolTraversal
{
    public void TraverseAllSymbols(INamespaceOrTypeSymbol symbol, int depth = 0)
    {
        var indent = new string(' ', depth * 2);
        
        if (symbol is INamespaceSymbol ns)
        {
            Console.WriteLine($"{indent}命名空间: {ns.Name}");
            
            foreach (var member in ns.GetMembers())
            {
                if (member is INamespaceOrTypeSymbol childSymbol)
                {
                    TraverseAllSymbols(childSymbol, depth + 1);
                }
            }
        }
        else if (symbol is INamedTypeSymbol type)
        {
            Console.WriteLine($"{indent}类型: {type.Name} ({type.TypeKind})");
            
            foreach (var member in type.GetMembers())
            {
                Console.WriteLine($"{indent}  成员: {member.Name} ({member.Kind})");
            }
        }
    }
}

遍历类的所有成员

csharp
public class MemberTraversal
{
    public void TraverseMembers(INamedTypeSymbol typeSymbol)
    {
        Console.WriteLine($"类: {typeSymbol.Name}");
        Console.WriteLine($"\n所有成员:");
        
        foreach (var member in typeSymbol.GetMembers())
        {
            Console.WriteLine($"  {member.Kind}: {member.ToDisplayString()}");
        }
        
        Console.WriteLine($"\n字段:");
        foreach (var field in typeSymbol.GetMembers().OfType<IFieldSymbol>())
        {
            Console.WriteLine($"  - {field.Name}: {field.Type.ToDisplayString()}");
        }
        
        Console.WriteLine($"\n属性:");
        foreach (var property in typeSymbol.GetMembers().OfType<IPropertySymbol>())
        {
            Console.WriteLine($"  - {property.Name}: {property.Type.ToDisplayString()}");
        }
        
        Console.WriteLine($"\n方法:");
        foreach (var method in typeSymbol.GetMembers().OfType<IMethodSymbol>())
        {
            if (method.MethodKind == MethodKind.Ordinary)
            {
                var parameters = string.Join(", ", 
                    method.Parameters.Select(p => p.Type.ToDisplayString()));
                Console.WriteLine($"  - {method.Name}({parameters})");
            }
        }
        
        Console.WriteLine($"\n构造函数:");
        foreach (var ctor in typeSymbol.Constructors)
        {
            Console.WriteLine($"  - {ctor.ToDisplayString()}");
        }
    }
}

符号查询

按名称查找成员

csharp
public class MemberLookup
{
    // 查找特定名称的成员
    public IEnumerable<ISymbol> FindMembersByName(INamedTypeSymbol typeSymbol, string name)
    {
        return typeSymbol.GetMembers(name);
    }
    
    // 查找特定名称的方法
    public IEnumerable<IMethodSymbol> FindMethodsByName(INamedTypeSymbol typeSymbol, string name)
    {
        return typeSymbol.GetMembers(name).OfType<IMethodSymbol>();
    }
    
    // 查找特定名称的属性
    public IPropertySymbol FindProperty(INamedTypeSymbol typeSymbol, string name)
    {
        return typeSymbol.GetMembers(name).OfType<IPropertySymbol>().FirstOrDefault();
    }
    
    // 查找特定名称的字段
    public IFieldSymbol FindField(INamedTypeSymbol typeSymbol, string name)
    {
        return typeSymbol.GetMembers(name).OfType<IFieldSymbol>().FirstOrDefault();
    }
}

查询特定符号

csharp
public class SymbolQuery
{
    // 查找所有公共类
    public IEnumerable<INamedTypeSymbol> FindPublicClasses(INamespaceSymbol ns)
    {
        foreach (var member in ns.GetMembers())
        {
            if (member is INamespaceSymbol childNs)
            {
                foreach (var type in FindPublicClasses(childNs))
                {
                    yield return type;
                }
            }
            else if (member is INamedTypeSymbol type && 
                     type.TypeKind == TypeKind.Class &&
                     type.DeclaredAccessibility == Accessibility.Public)
            {
                yield return type;
            }
        }
    }
    
    // 查找所有带特定特性的方法
    public IEnumerable<IMethodSymbol> FindMethodsWithAttribute(
        INamedTypeSymbol type, 
        string attributeName)
    {
        return type.GetMembers()
            .OfType<IMethodSymbol>()
            .Where(m => m.GetAttributes()
                .Any(a => a.AttributeClass?.Name == attributeName));
    }
    
    // 查找所有实现特定接口的类型
    public IEnumerable<INamedTypeSymbol> FindTypesImplementingInterface(
        INamespaceSymbol ns,
        string interfaceName)
    {
        foreach (var member in ns.GetMembers())
        {
            if (member is INamespaceSymbol childNs)
            {
                foreach (var type in FindTypesImplementingInterface(childNs, interfaceName))
                {
                    yield return type;
                }
            }
            else if (member is INamedTypeSymbol type)
            {
                if (type.AllInterfaces.Any(i => i.Name == interfaceName))
                {
                    yield return type;
                }
            }
        }
    }
    
    // 查找所有公共属性
    public IEnumerable<IPropertySymbol> FindPublicProperties(INamedTypeSymbol type)
    {
        return type.GetMembers()
            .OfType<IPropertySymbol>()
            .Where(p => p.DeclaredAccessibility == Accessibility.Public);
    }
}

符号比较

SymbolEqualityComparer 详解

csharp
public class SymbolComparison
{
    public void DemonstrateComparison()
    {
        var code = @"
            class Person { }
            class Person { }  // 不同的 Person
        ";
        
        var tree = CSharpSyntaxTree.ParseText(code);
        var compilation = CSharpCompilation.Create("temp")
            .AddReferences(MetadataReference.CreateFromFile(typeof(object).Assembly.Location))
            .AddSyntaxTrees(tree);
        
        var model = compilation.GetSemanticModel(tree);
        var root = tree.GetRoot();
        
        var classes = root.DescendantNodes()
            .OfType<ClassDeclarationSyntax>()
            .ToList();
        
        var symbol1 = model.GetDeclaredSymbol(classes[0]);
        var symbol2 = model.GetDeclaredSymbol(classes[1]);
        
        // ❌ 错误:使用 == 或 Equals
        Console.WriteLine($"使用 ==: {symbol1 == symbol2}");  // False(引用不同)
        Console.WriteLine($"使用 Equals: {symbol1.Equals(symbol2)}");  // False
        
        // ✅ 正确:使用 SymbolEqualityComparer
        var areEqual = SymbolEqualityComparer.Default.Equals(symbol1, symbol2);
        Console.WriteLine($"使用 SymbolEqualityComparer: {areEqual}");  // True(语义相同)
    }
}

⚠️ 重要

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

在集合中使用

csharp
public class SymbolCollections
{
    public void UseSymbolInCollections()
    {
        // 在 HashSet 中使用
        var symbolSet = new HashSet<ISymbol>(SymbolEqualityComparer.Default);
        symbolSet.Add(symbol1);
        symbolSet.Add(symbol2);  // 如果语义相同,不会添加
        
        // 在 Dictionary 中使用
        var symbolMap = new Dictionary<ISymbol, string>(SymbolEqualityComparer.Default);
        symbolMap[symbol1] = "First";
        symbolMap[symbol2] = "Second";  // 如果语义相同,会覆盖第一个
        
        // 检查是否包含
        bool contains = symbolSet.Contains(symbol);
        
        // 查找
        if (symbolMap.TryGetValue(symbol, out var value))
        {
            Console.WriteLine($"找到: {value}");
        }
    }
}

符号过滤

按访问级别过滤

csharp
public class AccessibilityFilter
{
    public IEnumerable<ISymbol> GetPublicMembers(INamedTypeSymbol type)
    {
        return type.GetMembers()
            .Where(m => m.DeclaredAccessibility == Accessibility.Public);
    }
    
    public IEnumerable<ISymbol> GetPrivateMembers(INamedTypeSymbol type)
    {
        return type.GetMembers()
            .Where(m => m.DeclaredAccessibility == Accessibility.Private);
    }
    
    public IEnumerable<ISymbol> GetProtectedMembers(INamedTypeSymbol type)
    {
        return type.GetMembers()
            .Where(m => m.DeclaredAccessibility == Accessibility.Protected);
    }
    
    public IEnumerable<ISymbol> GetInternalMembers(INamedTypeSymbol type)
    {
        return type.GetMembers()
            .Where(m => m.DeclaredAccessibility == Accessibility.Internal);
    }
}

按修饰符过滤

csharp
public class ModifierFilter
{
    public IEnumerable<ISymbol> GetStaticMembers(INamedTypeSymbol type)
    {
        return type.GetMembers().Where(m => m.IsStatic);
    }
    
    public IEnumerable<ISymbol> GetVirtualMembers(INamedTypeSymbol type)
    {
        return type.GetMembers().Where(m => m.IsVirtual);
    }
    
    public IEnumerable<ISymbol> GetAbstractMembers(INamedTypeSymbol type)
    {
        return type.GetMembers().Where(m => m.IsAbstract);
    }
    
    public IEnumerable<IMethodSymbol> GetAsyncMethods(INamedTypeSymbol type)
    {
        return type.GetMembers()
            .OfType<IMethodSymbol>()
            .Where(m => m.IsAsync);
    }
}

实用工具方法

符号检查工具

csharp
public static class SymbolHelpers
{
    // 检查符号是否是公共的
    public static bool IsPublic(ISymbol symbol)
    {
        return symbol.DeclaredAccessibility == Accessibility.Public;
    }
    
    // 检查符号是否是静态的
    public static bool IsStatic(ISymbol symbol)
    {
        return symbol.IsStatic;
    }
    
    // 检查符号是否是抽象的
    public static bool IsAbstract(ISymbol symbol)
    {
        return symbol.IsAbstract;
    }
    
    // 检查符号是否是虚方法
    public static bool IsVirtual(ISymbol symbol)
    {
        return symbol.IsVirtual;
    }
    
    // 检查符号是否是密封的
    public static bool IsSealed(ISymbol symbol)
    {
        return symbol.IsSealed;
    }
    
    // 获取符号的完整名称
    public static string GetFullName(ISymbol symbol)
    {
        return symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
    }
    
    // 获取符号的简短名称
    public static string GetShortName(ISymbol symbol)
    {
        return symbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat);
    }
}

类型检查工具

csharp
public static class TypeHelpers
{
    // 检查是否是类
    public static bool IsClass(ITypeSymbol type)
    {
        return type.TypeKind == TypeKind.Class;
    }
    
    // 检查是否是接口
    public static bool IsInterface(ITypeSymbol type)
    {
        return type.TypeKind == TypeKind.Interface;
    }
    
    // 检查是否是结构
    public static bool IsStruct(ITypeSymbol type)
    {
        return type.TypeKind == TypeKind.Struct;
    }
    
    // 检查是否是枚举
    public static bool IsEnum(ITypeSymbol type)
    {
        return type.TypeKind == TypeKind.Enum;
    }
    
    // 检查是否是委托
    public static bool IsDelegate(ITypeSymbol type)
    {
        return type.TypeKind == TypeKind.Delegate;
    }
    
    // 检查是否是泛型类型
    public static bool IsGenericType(ITypeSymbol type)
    {
        return type is INamedTypeSymbol namedType && namedType.IsGenericType;
    }
}

常见场景

场景 1: 查找类的所有构造函数

csharp
public IEnumerable<IMethodSymbol> GetConstructors(INamedTypeSymbol typeSymbol)
{
    return typeSymbol.Constructors;
}

场景 2: 检查方法是否重写了基类方法

csharp
public bool IsOverride(IMethodSymbol method)
{
    return method.IsOverride;
}

public IMethodSymbol GetOverriddenMethod(IMethodSymbol method)
{
    return method.OverriddenMethod;
}

场景 3: 获取符号的定义位置

csharp
public Location GetDefinitionLocation(ISymbol symbol)
{
    return symbol.Locations.FirstOrDefault();
}

public string GetDefinitionFilePath(ISymbol symbol)
{
    var location = symbol.Locations.FirstOrDefault();
    return location?.SourceTree?.FilePath;
}

场景 4: 查找所有扩展方法

csharp
public IEnumerable<IMethodSymbol> FindExtensionMethods(INamespaceSymbol ns)
{
    foreach (var member in ns.GetMembers())
    {
        if (member is INamespaceSymbol childNs)
        {
            foreach (var method in FindExtensionMethods(childNs))
            {
                yield return method;
            }
        }
        else if (member is INamedTypeSymbol type)
        {
            foreach (var method in type.GetMembers().OfType<IMethodSymbol>())
            {
                if (method.IsExtensionMethod)
                {
                    yield return method;
                }
            }
        }
    }
}

下一步


🔗 相关资源


最后更新: 2025-01-21

基于 MIT 许可发布