符号系统
📚 文档导航
本文档是符号系统系列的主文档,介绍基础概念和快速入门。
📖 文档系列
| 文档 | 内容 | 难度 |
|---|---|---|
| 符号系统基础 ⭐ | 基础概念、符号层次、快速入门 | 🟢 入门 |
| 符号类型详解 | 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 参考
💡 实战示例
- Builder 生成器 - 使用符号生成 Builder 模式代码
- 增量生成器示例 - 在增量生成器中使用符号
🔗 外部资源
最后更新: 2025-01-21