符号操作详解
📚 文档导航
本文档详细介绍符号的获取、遍历、查询和比较操作。
📖 文档系列
| 文档 | 内容 | 难度 |
|---|---|---|
| 符号系统基础 | 基础概念、符号层次、快速入门 | 🟢 入门 |
| 符号类型详解 | 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;
}
}
}
}
}下一步
🔗 相关资源
- 符号系统基础 - 返回主文档
- 语义模型 - 了解如何使用语义模型访问符号
- 符号 API(官方文档)
最后更新: 2025-01-21