符号查找和导航
深入理解 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
}
}🔑 关键要点
- 作用域查找: 使用
LookupSymbols查找指定位置可访问的符号 - 类型查找: 使用
GetTypeByMetadataName,泛型类型需要`n后缀 - 缓存模型: 缓存
SemanticModel以提高性能 - 空值检查: 始终检查查找结果是否为 null
- 符号比较: 使用
SymbolEqualityComparer比较符号
📖 相关文档
🚀 下一步
- 学习 最佳实践
- 了解实际应用场景
- 探索高级符号操作
最后更新: 2026-02-05