Skip to content

符号查找和导航

深入理解 Roslyn 中的符号查找、导航和定位方法

📋 文档信息

属性
难度中级
阅读时间20 分钟
前置知识C# 命名空间、作用域
相关文档ISymbol 基础符号比较

🎯 学习目标

完成本文档后,你将能够:

  • ✅ 使用 SemanticModel 查找作用域内的符号
  • ✅ 从 Compilation 查找类型和命名空间
  • ✅ 导航符号的包含关系
  • ✅ 查找方法的重载
  • ✅ 查找类型的派生类和实现

📚 快速导航

主题说明
作用域查找使用 LookupSymbols 查找符号
类型查找从 Compilation 查找类型
符号导航导航符号的包含关系
高级查找查找派生类、实现等
完整示例实用的符号导航器
最佳实践符号查找的最佳实践

作用域内符号查找

使用 SemanticModel.LookupSymbols 查找指定位置可访问的符号。

LookupSymbols 基础

csharp
SemanticModel semanticModel;
int position = /* 代码位置 */;

// ============================================================
// 1. 查找所有可访问的符号
// ============================================================

ImmutableArray<ISymbol> symbols = semanticModel.LookupSymbols(position);

// ============================================================
// 2. 按名称查找
// ============================================================

ImmutableArray<ISymbol> namedSymbols = 
    semanticModel.LookupSymbols(position, name: "MyMethod");

// ============================================================
// 3. 在特定容器中查找
// ============================================================

INamespaceOrTypeSymbol container = /* ... */;
ImmutableArray<ISymbol> containerSymbols = 
    semanticModel.LookupSymbols(position, container: container);

// ============================================================
// 4. 按名称在容器中查找
// ============================================================

ImmutableArray<ISymbol> specificSymbols = 
    semanticModel.LookupSymbols(
        position, 
        container: container, 
        name: "MyMethod");

作用域查找器

csharp
/// <summary>
/// 作用域符号查找器
/// </summary>
public class ScopeLookup
{
    private readonly SemanticModel _semanticModel;
    
    public ScopeLookup(SemanticModel semanticModel)
    {
        _semanticModel = semanticModel;
    }
    
    /// <summary>
    /// 查找指定位置的所有可访问符号
    /// </summary>
    public List<ISymbol> FindAccessibleSymbols(int position)
    {
        return _semanticModel.LookupSymbols(position).ToList();
    }
    
    /// <summary>
    /// 查找指定名称的符号
    /// </summary>
    public List<ISymbol> FindSymbolsByName(int position, string name)
    {
        return _semanticModel.LookupSymbols(position, name: name).ToList();
    }
    
    /// <summary>
    /// 查找指定类型的符号
    /// </summary>
    public List<T> FindSymbolsOfType<T>(int position) where T : ISymbol
    {
        return _semanticModel.LookupSymbols(position)
            .OfType<T>()
            .ToList();
    }
    
    /// <summary>
    /// 查找可访问的方法
    /// </summary>
    public List<IMethodSymbol> FindAccessibleMethods(int position)
    {
        return _semanticModel.LookupSymbols(position)
            .OfType<IMethodSymbol>()
            .Where(m => m.MethodKind == MethodKind.Ordinary)
            .ToList();
    }
    
    /// <summary>
    /// 查找可访问的类型
    /// </summary>
    public List<INamedTypeSymbol> FindAccessibleTypes(int position)
    {
        return _semanticModel.LookupSymbols(position)
            .OfType<INamedTypeSymbol>()
            .ToList();
    }
}

从 Compilation 查找类型

使用 Compilation 查找类型和特殊类型。

类型查找方法

csharp
Compilation compilation;

// ============================================================
// 1. 按完全限定名查找类型
// ============================================================

// 非泛型类型
INamedTypeSymbol stringType = 
    compilation.GetTypeByMetadataName("System.String");

// 泛型类型(使用 `n 表示泛型参数数量)
INamedTypeSymbol listType = 
    compilation.GetTypeByMetadataName("System.Collections.Generic.List`1");

INamedTypeSymbol dictionaryType = 
    compilation.GetTypeByMetadataName("System.Collections.Generic.Dictionary`2");

// ============================================================
// 2. 获取特殊类型
// ============================================================

INamedTypeSymbol intType = 
    compilation.GetSpecialType(SpecialType.System_Int32);

INamedTypeSymbol stringType2 = 
    compilation.GetSpecialType(SpecialType.System_String);

INamedTypeSymbol objectType = 
    compilation.GetSpecialType(SpecialType.System_Object);

// ============================================================
// 3. 获取全局命名空间
// ============================================================

INamespaceSymbol globalNamespace = compilation.GlobalNamespace;

类型查找器

csharp
/// <summary>
/// 类型查找器
/// </summary>
public class TypeFinder
{
    private readonly Compilation _compilation;
    
    public TypeFinder(Compilation compilation)
    {
        _compilation = compilation;
    }
    
    /// <summary>
    /// 查找类型(支持泛型)
    /// </summary>
    public INamedTypeSymbol FindType(string fullyQualifiedName)
    {
        // 处理泛型类型名称
        // 例如: "System.Collections.Generic.List`1"
        return _compilation.GetTypeByMetadataName(fullyQualifiedName);
    }
    
    /// <summary>
    /// 查找泛型类型
    /// </summary>
    public INamedTypeSymbol FindGenericType(string namespaceName, string typeName, int arity)
    {
        var fullName = $"{namespaceName}.{typeName}`{arity}";
        return _compilation.GetTypeByMetadataName(fullName);
    }
    
    /// <summary>
    /// 查找命名空间中的所有类型
    /// </summary>
    public List<INamedTypeSymbol> GetTypesInNamespace(string namespaceName)
    {
        var types = new List<INamedTypeSymbol>();
        var namespaceSymbol = FindNamespace(namespaceName);
        
        if (namespaceSymbol != null)
        {
            CollectTypes(namespaceSymbol, types);
        }
        
        return types;
    }
    
    /// <summary>
    /// 递归收集命名空间中的所有类型
    /// </summary>
    private void CollectTypes(
        INamespaceSymbol namespaceSymbol, 
        List<INamedTypeSymbol> types)
    {
        // 添加当前命名空间的类型
        types.AddRange(namespaceSymbol.GetTypeMembers());
        
        // 递归处理子命名空间
        foreach (var childNamespace in namespaceSymbol.GetNamespaceMembers())
        {
            CollectTypes(childNamespace, types);
        }
    }
    
    /// <summary>
    /// 查找命名空间
    /// </summary>
    private INamespaceSymbol FindNamespace(string namespaceName)
    {
        var parts = namespaceName.Split('.');
        var current = _compilation.GlobalNamespace;
        
        foreach (var part in parts)
        {
            current = current.GetNamespaceMembers()
                .FirstOrDefault(ns => ns.Name == part);
            
            if (current == null)
                return null;
        }
        
        return current;
    }
}

符号导航

从符号导航到相关的其他符号。

基本导航

csharp
ISymbol symbol;

// ============================================================
// 包含关系
// ============================================================

// 获取包含的符号(父符号)
ISymbol containingSymbol = symbol.ContainingSymbol;
INamedTypeSymbol containingType = symbol.ContainingType;
INamespaceSymbol containingNamespace = symbol.ContainingNamespace;
IAssemblySymbol containingAssembly = symbol.ContainingAssembly;

// ============================================================
// 类型成员
// ============================================================

INamedTypeSymbol typeSymbol;

// 获取所有成员
ImmutableArray<ISymbol> members = typeSymbol.GetMembers();

// 按名称获取成员
ImmutableArray<ISymbol> namedMembers = typeSymbol.GetMembers("MethodName");

// 获取类型成员(嵌套类型)
ImmutableArray<INamedTypeSymbol> typeMembers = typeSymbol.GetTypeMembers();

// 按名称获取类型成员
ImmutableArray<INamedTypeSymbol> namedTypeMembers = 
    typeSymbol.GetTypeMembers("NestedClass");

// ============================================================
// 命名空间成员
// ============================================================

INamespaceSymbol namespaceSymbol;

// 获取所有成员
ImmutableArray<ISymbol> namespaceMembers = namespaceSymbol.GetMembers();

// 获取子命名空间
ImmutableArray<INamespaceSymbol> childNamespaces = 
    namespaceSymbol.GetNamespaceMembers();

// 获取类型
ImmutableArray<INamedTypeSymbol> types = 
    namespaceSymbol.GetTypeMembers();

符号导航器

csharp
/// <summary>
/// 符号导航器
/// </summary>
public class SymbolNavigator
{
    /// <summary>
    /// 获取符号的完整路径
    /// </summary>
    public string GetSymbolPath(ISymbol symbol)
    {
        var parts = new List<string>();
        var current = symbol;
        
        while (current != null && !current.ContainingNamespace.IsGlobalNamespace)
        {
            parts.Insert(0, current.Name);
            current = current.ContainingSymbol;
        }
        
        return string.Join(".", parts);
    }
    
    /// <summary>
    /// 获取类型的所有成员(包括继承的)
    /// </summary>
    public List<ISymbol> GetAllMembers(INamedTypeSymbol type)
    {
        var members = new List<ISymbol>();
        var current = type;
        
        while (current != null && 
               current.SpecialType != SpecialType.System_Object)
        {
            members.AddRange(current.GetMembers());
            current = current.BaseType;
        }
        
        return members;
    }
    
    /// <summary>
    /// 查找方法的所有重载
    /// </summary>
    public List<IMethodSymbol> FindMethodOverloads(
        INamedTypeSymbol typeSymbol, 
        string methodName)
    {
        return typeSymbol.GetMembers(methodName)
            .OfType<IMethodSymbol>()
            .Where(m => m.MethodKind == MethodKind.Ordinary)
            .ToList();
    }
    
    /// <summary>
    /// 查找属性的后备字段
    /// </summary>
    public IFieldSymbol FindBackingField(IPropertySymbol property)
    {
        if (!property.IsAutoProperty)
            return null;
        
        // 自动属性的后备字段通常命名为 <PropertyName>k__BackingField
        var backingFieldName = $"<{property.Name}>k__BackingField";
        
        return property.ContainingType.GetMembers(backingFieldName)
            .OfType<IFieldSymbol>()
            .FirstOrDefault();
    }
    
    /// <summary>
    /// 获取命名空间的完整路径
    /// </summary>
    public string GetNamespacePath(INamespaceSymbol namespaceSymbol)
    {
        if (namespaceSymbol.IsGlobalNamespace)
            return "<global>";
        
        var parts = new List<string>();
        var current = namespaceSymbol;
        
        while (current != null && !current.IsGlobalNamespace)
        {
            parts.Insert(0, current.Name);
            current = current.ContainingNamespace;
        }
        
        return string.Join(".", parts);
    }
}

高级查找场景

查找派生类

csharp
/// <summary>
/// 查找类型的所有派生类
/// </summary>
public class DerivedTypeFinder
{
    private readonly Compilation _compilation;
    
    public DerivedTypeFinder(Compilation compilation)
    {
        _compilation = compilation;
    }
    
    /// <summary>
    /// 查找所有派生类
    /// </summary>
    public List<INamedTypeSymbol> FindDerivedTypes(INamedTypeSymbol baseType)
    {
        var derivedTypes = new List<INamedTypeSymbol>();
        
        // 遍历编译中的所有类型
        foreach (var syntaxTree in _compilation.SyntaxTrees)
        {
            var semanticModel = _compilation.GetSemanticModel(syntaxTree);
            var root = syntaxTree.GetRoot();
            
            var classDeclarations = root.DescendantNodes()
                .OfType<ClassDeclarationSyntax>();
            
            foreach (var classDecl in classDeclarations)
            {
                var classSymbol = semanticModel.GetDeclaredSymbol(classDecl);
                if (classSymbol != null && 
                    InheritsFrom(classSymbol, baseType))
                {
                    derivedTypes.Add(classSymbol);
                }
            }
        }
        
        return derivedTypes;
    }
    
    /// <summary>
    /// 检查类型是否继承自指定基类
    /// </summary>
    private bool InheritsFrom(ITypeSymbol type, ITypeSymbol baseType)
    {
        var current = type.BaseType;
        while (current != null)
        {
            if (SymbolEqualityComparer.Default.Equals(current, baseType))
                return true;
            
            current = current.BaseType;
        }
        
        return false;
    }
}

查找接口实现

csharp
/// <summary>
/// 查找实现了特定接口的所有类型
/// </summary>
public class InterfaceImplementationFinder
{
    private readonly Compilation _compilation;
    
    public InterfaceImplementationFinder(Compilation compilation)
    {
        _compilation = compilation;
    }
    
    /// <summary>
    /// 查找所有实现
    /// </summary>
    public List<INamedTypeSymbol> FindImplementations(INamedTypeSymbol interfaceType)
    {
        var implementations = new List<INamedTypeSymbol>();
        
        foreach (var syntaxTree in _compilation.SyntaxTrees)
        {
            var semanticModel = _compilation.GetSemanticModel(syntaxTree);
            var root = syntaxTree.GetRoot();
            
            var typeDeclarations = root.DescendantNodes()
                .Where(n => n is ClassDeclarationSyntax || 
                           n is StructDeclarationSyntax);
            
            foreach (var typeDecl in typeDeclarations)
            {
                var typeSymbol = semanticModel.GetDeclaredSymbol(typeDecl) 
                    as INamedTypeSymbol;
                
                if (typeSymbol != null && 
                    typeSymbol.AllInterfaces.Any(i => 
                        SymbolEqualityComparer.Default.Equals(i, interfaceType)))
                {
                    implementations.Add(typeSymbol);
                }
            }
        }
        
        return implementations;
    }
}

完整示例:符号导航器

csharp
/// <summary>
/// 完整的符号导航器
/// </summary>
public class ComprehensiveSymbolNavigator
{
    private readonly Compilation _compilation;
    
    public ComprehensiveSymbolNavigator(Compilation compilation)
    {
        _compilation = compilation;
    }
    
    /// <summary>
    /// 查找类型(支持泛型)
    /// </summary>
    public INamedTypeSymbol FindType(string fullyQualifiedName)
    {
        return _compilation.GetTypeByMetadataName(fullyQualifiedName);
    }
    
    /// <summary>
    /// 查找命名空间
    /// </summary>
    public INamespaceSymbol FindNamespace(string namespaceName)
    {
        var parts = namespaceName.Split('.');
        var current = _compilation.GlobalNamespace;
        
        foreach (var part in parts)
        {
            current = current.GetNamespaceMembers()
                .FirstOrDefault(ns => ns.Name == part);
            
            if (current == null)
                return null;
        }
        
        return current;
    }
    
    /// <summary>
    /// 查找命名空间中的所有类型
    /// </summary>
    public List<INamedTypeSymbol> GetTypesInNamespace(string namespaceName)
    {
        var types = new List<INamedTypeSymbol>();
        var namespaceSymbol = FindNamespace(namespaceName);
        
        if (namespaceSymbol != null)
        {
            CollectTypes(namespaceSymbol, types);
        }
        
        return types;
    }
    
    /// <summary>
    /// 递归收集命名空间中的所有类型
    /// </summary>
    private void CollectTypes(
        INamespaceSymbol namespaceSymbol, 
        List<INamedTypeSymbol> types)
    {
        types.AddRange(namespaceSymbol.GetTypeMembers());
        
        foreach (var childNamespace in namespaceSymbol.GetNamespaceMembers())
        {
            CollectTypes(childNamespace, types);
        }
    }
    
    /// <summary>
    /// 查找方法的所有重载
    /// </summary>
    public List<IMethodSymbol> FindMethodOverloads(
        INamedTypeSymbol typeSymbol, 
        string methodName)
    {
        return typeSymbol.GetMembers(methodName)
            .OfType<IMethodSymbol>()
            .Where(m => m.MethodKind == MethodKind.Ordinary)
            .ToList();
    }
    
    /// <summary>
    /// 查找类型的所有派生类
    /// </summary>
    public List<INamedTypeSymbol> FindDerivedTypes(INamedTypeSymbol baseType)
    {
        var derivedTypes = new List<INamedTypeSymbol>();
        
        foreach (var syntaxTree in _compilation.SyntaxTrees)
        {
            var semanticModel = _compilation.GetSemanticModel(syntaxTree);
            var root = syntaxTree.GetRoot();
            
            var classDeclarations = root.DescendantNodes()
                .OfType<ClassDeclarationSyntax>();
            
            foreach (var classDecl in classDeclarations)
            {
                var classSymbol = semanticModel.GetDeclaredSymbol(classDecl);
                if (classSymbol != null && 
                    InheritsFrom(classSymbol, baseType))
                {
                    derivedTypes.Add(classSymbol);
                }
            }
        }
        
        return derivedTypes;
    }
    
    /// <summary>
    /// 查找实现了特定接口的所有类型
    /// </summary>
    public List<INamedTypeSymbol> FindImplementations(INamedTypeSymbol interfaceType)
    {
        var implementations = new List<INamedTypeSymbol>();
        
        foreach (var syntaxTree in _compilation.SyntaxTrees)
        {
            var semanticModel = _compilation.GetSemanticModel(syntaxTree);
            var root = syntaxTree.GetRoot();
            
            var typeDeclarations = root.DescendantNodes()
                .Where(n => n is ClassDeclarationSyntax || 
                           n is StructDeclarationSyntax);
            
            foreach (var typeDecl in typeDeclarations)
            {
                var typeSymbol = semanticModel.GetDeclaredSymbol(typeDecl) 
                    as INamedTypeSymbol;
                
                if (typeSymbol != null && 
                    typeSymbol.AllInterfaces.Any(i => 
                        SymbolEqualityComparer.Default.Equals(i, interfaceType)))
                {
                    implementations.Add(typeSymbol);
                }
            }
        }
        
        return implementations;
    }
    
    /// <summary>
    /// 检查类型是否继承自指定基类
    /// </summary>
    private bool InheritsFrom(ITypeSymbol type, ITypeSymbol baseType)
    {
        var current = type.BaseType;
        while (current != null)
        {
            if (SymbolEqualityComparer.Default.Equals(current, baseType))
                return true;
            
            current = current.BaseType;
        }
        
        return false;
    }
}

最佳实践

✅ 推荐做法

csharp
/// <summary>
/// 符号查找的最佳实践
/// </summary>
public class LookupBestPractices
{
    // ✅ 1. 缓存 SemanticModel
    private readonly Dictionary<SyntaxTree, SemanticModel> _cache = new();
    
    public SemanticModel GetSemanticModel(Compilation compilation, SyntaxTree tree)
    {
        if (!_cache.TryGetValue(tree, out var model))
        {
            model = compilation.GetSemanticModel(tree);
            _cache[tree] = model;
        }
        return model;
    }
    
    // ✅ 2. 使用正确的泛型类型名称
    public INamedTypeSymbol FindGenericType(Compilation compilation)
    {
        // ✅ 正确:使用 `n 表示泛型参数数量
        return compilation.GetTypeByMetadataName(
            "System.Collections.Generic.List`1");
    }
    
    // ✅ 3. 检查查找结果是否为 null
    public void SafeLookup(Compilation compilation)
    {
        var type = compilation.GetTypeByMetadataName("MyNamespace.MyType");
        
        // ✅ 正确:检查 null
        if (type != null)
        {
            // 使用 type
        }
    }
    
    // ✅ 4. 使用 SymbolEqualityComparer
    public bool CompareSymbols(ISymbol symbol1, ISymbol symbol2)
    {
        return SymbolEqualityComparer.Default.Equals(symbol1, symbol2);
    }
}

❌ 应避免的做法

csharp
/// <summary>
/// 应该避免的反模式
/// </summary>
public class LookupAntiPatterns
{
    // ❌ 1. 重复创建 SemanticModel
    public void WrongSemanticModelUsage(Compilation compilation, List<SyntaxTree> trees)
    {
        foreach (var tree in trees)
        {
            // ❌ 错误:每次都创建新的 SemanticModel
            var model = compilation.GetSemanticModel(tree);
        }
    }
    
    // ❌ 2. 错误的泛型类型名称
    public void WrongGenericTypeName(Compilation compilation)
    {
        // ❌ 错误:缺少 `1
        var type = compilation.GetTypeByMetadataName(
            "System.Collections.Generic.List");
    }
    
    // ❌ 3. 不检查 null
    public void WrongNullCheck(Compilation compilation)
    {
        // ❌ 错误:可能返回 null
        var type = compilation.GetTypeByMetadataName("MyType");
        var members = type.GetMembers(); // 可能 NullReferenceException
    }
}

🔑 关键要点

  1. 作用域查找: 使用 LookupSymbols 查找指定位置可访问的符号
  2. 类型查找: 使用 GetTypeByMetadataName,泛型类型需要 `n 后缀
  3. 缓存模型: 缓存 SemanticModel 以提高性能
  4. 空值检查: 始终检查查找结果是否为 null
  5. 符号比较: 使用 SymbolEqualityComparer 比较符号

📖 相关文档


🚀 下一步

  • 学习 最佳实践
  • 了解实际应用场景
  • 探索高级符号操作

最后更新: 2026-02-05

基于 MIT 许可发布