符号比较和等价性
深入理解 Roslyn 中的符号比较、类型转换和等价性检查
📋 文档信息
| 属性 | 值 |
|---|---|
| 难度 | 中级 |
| 阅读时间 | 20 分钟 |
| 前置知识 | C# 类型系统、继承、接口 |
| 相关文档 | ISymbol 基础、ITypeSymbol |
🎯 学习目标
完成本文档后,你将能够:
- ✅ 正确使用 SymbolEqualityComparer 比较符号
- ✅ 检查类型的继承和接口实现关系
- ✅ 分析类型转换的可行性
- ✅ 查找类型的公共基类
- ✅ 在集合中正确使用符号
📚 快速导航
| 主题 | 说明 |
|---|---|
| SymbolEqualityComparer | 符号比较器的使用 |
| 类型比较 | 比较类型符号 |
| 继承关系 | 检查继承和接口实现 |
| 类型转换 | 分析类型转换 |
| 完整示例 | 实用的类型关系分析器 |
| 最佳实践 | 符号比较的最佳实践 |
SymbolEqualityComparer
在 Roslyn 中,同一个符号可能有多个实例,因此必须使用 SymbolEqualityComparer 来比较符号。
基本用法
csharp
ISymbol symbol1, symbol2;
// ============================================================
// 基本比较
// ============================================================
// ✅ 正确:使用 SymbolEqualityComparer
bool areEqual = SymbolEqualityComparer.Default.Equals(symbol1, symbol2);
// ❌ 错误:不要使用 ==
// bool areEqual = symbol1 == symbol2; // 不可靠!
// ============================================================
// 不同的比较器
// ============================================================
// 默认比较器(考虑泛型参数)
bool equal1 = SymbolEqualityComparer.Default.Equals(symbol1, symbol2);
// 包含可空性的比较
bool equal2 = SymbolEqualityComparer.IncludeNullability.Equals(symbol1, symbol2);
// 忽略泛型参数的比较
// 例如:List<int> 和 List<string> 被认为是相等的
bool equal3 = SymbolEqualityComparer.Default.Equals(
type1.OriginalDefinition,
type2.OriginalDefinition);在集合中使用
csharp
/// <summary>
/// 在集合中使用 SymbolEqualityComparer
/// </summary>
public class SymbolCollectionUsage
{
// ============================================================
// HashSet
// ============================================================
public void UseHashSet()
{
// ✅ 正确:指定比较器
var symbolSet = new HashSet<ISymbol>(SymbolEqualityComparer.Default);
ISymbol symbol1, symbol2;
symbolSet.Add(symbol1);
bool contains = symbolSet.Contains(symbol2);
}
// ============================================================
// Dictionary
// ============================================================
public void UseDictionary()
{
// ✅ 正确:指定比较器
var symbolDict = new Dictionary<ISymbol, string>(
SymbolEqualityComparer.Default);
ISymbol symbol;
symbolDict[symbol] = "value";
}
// ============================================================
// LINQ 操作
// ============================================================
public void UseLinq(IEnumerable<ISymbol> symbols)
{
// Distinct
var uniqueSymbols = symbols.Distinct(SymbolEqualityComparer.Default);
// GroupBy
var grouped = symbols.GroupBy(
s => s,
SymbolEqualityComparer.Default);
// Contains
ISymbol targetSymbol = null;
bool hasSymbol = symbols.Contains(
targetSymbol,
SymbolEqualityComparer.Default);
}
}类型比较
比较类型符号时需要特别注意泛型类型。
基本类型比较
csharp
ITypeSymbol type1, type2;
// ============================================================
// 完全相等
// ============================================================
// ✅ 正确:使用 SymbolEqualityComparer
bool areEqual = SymbolEqualityComparer.Default.Equals(type1, type2);
// ============================================================
// 特殊类型检查
// ============================================================
// 检查是否是特定的内置类型
bool isString = type1.SpecialType == SpecialType.System_String;
bool isInt = type1.SpecialType == SpecialType.System_Int32;
// ============================================================
// 类型种类检查
// ============================================================
bool isClass = type1.TypeKind == TypeKind.Class;
bool isInterface = type1.TypeKind == TypeKind.Interface;
bool isStruct = type1.TypeKind == TypeKind.Struct;
bool isEnum = type1.TypeKind == TypeKind.Enum;
// ============================================================
// 值类型和引用类型
// ============================================================
bool isValueType = type1.IsValueType;
bool isReferenceType = type1.IsReferenceType;泛型类型比较
csharp
/// <summary>
/// 泛型类型比较工具
/// </summary>
public class GenericTypeComparer
{
/// <summary>
/// 检查是否是同一个泛型定义
/// </summary>
public bool IsSameGenericDefinition(
INamedTypeSymbol type1,
INamedTypeSymbol type2)
{
if (!type1.IsGenericType || !type2.IsGenericType)
return false;
// 比较原始泛型定义
return SymbolEqualityComparer.Default.Equals(
type1.ConstructedFrom,
type2.ConstructedFrom);
}
/// <summary>
/// 检查是否是特定泛型类型的实例
/// 例如:检查 List<int> 是否是 List<T> 的实例
/// </summary>
public bool IsInstanceOfGenericType(
INamedTypeSymbol type,
INamedTypeSymbol genericDefinition)
{
if (!type.IsGenericType)
return false;
return SymbolEqualityComparer.Default.Equals(
type.ConstructedFrom,
genericDefinition);
}
/// <summary>
/// 比较泛型类型参数
/// </summary>
public bool HaveSameTypeArguments(
INamedTypeSymbol type1,
INamedTypeSymbol type2)
{
if (type1.TypeArguments.Length != type2.TypeArguments.Length)
return false;
for (int i = 0; i < type1.TypeArguments.Length; i++)
{
if (!SymbolEqualityComparer.Default.Equals(
type1.TypeArguments[i],
type2.TypeArguments[i]))
{
return false;
}
}
return true;
}
}继承和接口检查
检查类型之间的继承和接口实现关系。
继承关系检查
csharp
/// <summary>
/// 继承关系检查器
/// </summary>
public class InheritanceChecker
{
/// <summary>
/// 检查类型是否继承自指定基类
/// </summary>
public bool InheritsFrom(ITypeSymbol type, ITypeSymbol baseType)
{
var current = type;
while (current != null)
{
if (SymbolEqualityComparer.Default.Equals(current, baseType))
return true;
current = current.BaseType;
}
return false;
}
/// <summary>
/// 获取类型的所有基类
/// </summary>
public List<INamedTypeSymbol> GetBaseTypes(ITypeSymbol type)
{
var baseTypes = new List<INamedTypeSymbol>();
var current = type.BaseType;
while (current != null &&
current.SpecialType != SpecialType.System_Object)
{
baseTypes.Add(current);
current = current.BaseType;
}
return baseTypes;
}
/// <summary>
/// 获取继承链(包括自身)
/// </summary>
public List<INamedTypeSymbol> GetInheritanceChain(ITypeSymbol type)
{
var chain = new List<INamedTypeSymbol>();
if (type is INamedTypeSymbol namedType)
{
var current = namedType;
while (current != null &&
current.SpecialType != SpecialType.System_Object)
{
chain.Add(current);
current = current.BaseType;
}
}
return chain;
}
}接口实现检查
csharp
/// <summary>
/// 接口实现检查器
/// </summary>
public class InterfaceChecker
{
/// <summary>
/// 检查类型是否实现了指定接口
/// </summary>
public bool ImplementsInterface(
ITypeSymbol type,
ITypeSymbol interfaceType)
{
return type.AllInterfaces.Any(i =>
SymbolEqualityComparer.Default.Equals(i, interfaceType));
}
/// <summary>
/// 检查类型是否实现了指定的泛型接口
/// 例如:检查是否实现了 IEnumerable<T>
/// </summary>
public bool ImplementsGenericInterface(
ITypeSymbol type,
INamedTypeSymbol genericInterfaceDefinition)
{
return type.AllInterfaces.Any(i =>
i is INamedTypeSymbol namedInterface &&
namedInterface.IsGenericType &&
SymbolEqualityComparer.Default.Equals(
namedInterface.ConstructedFrom,
genericInterfaceDefinition));
}
/// <summary>
/// 获取实现的特定泛型接口
/// </summary>
public INamedTypeSymbol GetImplementedGenericInterface(
ITypeSymbol type,
INamedTypeSymbol genericInterfaceDefinition)
{
return type.AllInterfaces
.OfType<INamedTypeSymbol>()
.FirstOrDefault(i =>
i.IsGenericType &&
SymbolEqualityComparer.Default.Equals(
i.ConstructedFrom,
genericInterfaceDefinition));
}
/// <summary>
/// 获取所有实现的接口(包括基类的接口)
/// </summary>
public List<INamedTypeSymbol> GetAllInterfaces(ITypeSymbol type)
{
return type.AllInterfaces.ToList();
}
}类型转换分析
分析类型之间的转换关系。
Conversion 类型
csharp
Compilation compilation;
ITypeSymbol sourceType, targetType;
// ============================================================
// 分类类型转换
// ============================================================
Conversion conversion = compilation.ClassifyConversion(sourceType, targetType);
// 转换是否存在
bool exists = conversion.Exists;
// 转换类型
bool isIdentity = conversion.IsIdentity; // 相同类型
bool isImplicit = conversion.IsImplicit; // 隐式转换
bool isExplicit = conversion.IsExplicit; // 显式转换
bool isReference = conversion.IsReference; // 引用转换
bool isNumeric = conversion.IsNumeric; // 数值转换
bool isBoxing = conversion.IsBoxing; // 装箱
bool isUnboxing = conversion.IsUnboxing; // 拆箱
bool isNullable = conversion.IsNullable; // 可空转换
bool isUserDefined = conversion.IsUserDefined; // 用户定义的转换类型转换分析器
csharp
/// <summary>
/// 类型转换分析器
/// </summary>
public class ConversionAnalyzer
{
private readonly Compilation _compilation;
public ConversionAnalyzer(Compilation compilation)
{
_compilation = compilation;
}
/// <summary>
/// 分析类型转换
/// </summary>
public ConversionInfo AnalyzeConversion(
ITypeSymbol sourceType,
ITypeSymbol targetType)
{
var conversion = _compilation.ClassifyConversion(sourceType, targetType);
return new ConversionInfo
{
Exists = conversion.Exists,
IsIdentity = conversion.IsIdentity,
IsImplicit = conversion.IsImplicit,
IsExplicit = conversion.IsExplicit,
IsReference = conversion.IsReference,
IsNumeric = conversion.IsNumeric,
IsBoxing = conversion.IsBoxing,
IsUnboxing = conversion.IsUnboxing,
IsNullable = conversion.IsNullable,
IsUserDefined = conversion.IsUserDefined
};
}
/// <summary>
/// 检查是否可以隐式转换
/// </summary>
public bool CanConvertImplicitly(
ITypeSymbol sourceType,
ITypeSymbol targetType)
{
var conversion = _compilation.ClassifyConversion(sourceType, targetType);
return conversion.IsImplicit;
}
/// <summary>
/// 检查是否可以显式转换
/// </summary>
public bool CanConvertExplicitly(
ITypeSymbol sourceType,
ITypeSymbol targetType)
{
var conversion = _compilation.ClassifyConversion(sourceType, targetType);
return conversion.IsExplicit;
}
/// <summary>
/// 检查是否可以赋值
/// </summary>
public bool IsAssignableFrom(
ITypeSymbol targetType,
ITypeSymbol sourceType)
{
return _compilation.HasImplicitConversion(sourceType, targetType);
}
}
public class ConversionInfo
{
public bool Exists { get; set; }
public bool IsIdentity { get; set; }
public bool IsImplicit { get; set; }
public bool IsExplicit { get; set; }
public bool IsReference { get; set; }
public bool IsNumeric { get; set; }
public bool IsBoxing { get; set; }
public bool IsUnboxing { get; set; }
public bool IsNullable { get; set; }
public bool IsUserDefined { get; set; }
}完整示例:类型关系分析器
csharp
/// <summary>
/// 完整的类型关系分析器
/// </summary>
public class TypeRelationshipAnalyzer
{
private readonly Compilation _compilation;
public TypeRelationshipAnalyzer(Compilation compilation)
{
_compilation = compilation;
}
/// <summary>
/// 分析两个类型之间的关系
/// </summary>
public TypeRelationship AnalyzeRelationship(
ITypeSymbol type1,
ITypeSymbol type2)
{
var relationship = new TypeRelationship
{
Type1 = type1.ToDisplayString(),
Type2 = type2.ToDisplayString()
};
// 相等性
relationship.AreEqual = SymbolEqualityComparer.Default.Equals(type1, type2);
// 继承关系
relationship.Type1InheritsFromType2 = InheritsFrom(type1, type2);
relationship.Type2InheritsFromType1 = InheritsFrom(type2, type1);
// 接口实现
if (type2.TypeKind == TypeKind.Interface)
{
relationship.Type1ImplementsType2 = ImplementsInterface(type1, type2);
}
if (type1.TypeKind == TypeKind.Interface)
{
relationship.Type2ImplementsType1 = ImplementsInterface(type2, type1);
}
// 类型转换
var conversion1to2 = _compilation.ClassifyConversion(type1, type2);
relationship.CanConvertType1ToType2Implicitly = conversion1to2.IsImplicit;
relationship.CanConvertType1ToType2Explicitly = conversion1to2.IsExplicit;
var conversion2to1 = _compilation.ClassifyConversion(type2, type1);
relationship.CanConvertType2ToType1Implicitly = conversion2to1.IsImplicit;
relationship.CanConvertType2ToType1Explicitly = conversion2to1.IsExplicit;
// 公共基类
relationship.CommonBaseType = FindCommonBaseType(type1, type2)?.ToDisplayString();
return relationship;
}
/// <summary>
/// 检查类型是否继承自指定基类
/// </summary>
private bool InheritsFrom(ITypeSymbol type, ITypeSymbol baseType)
{
var current = type;
while (current != null)
{
if (SymbolEqualityComparer.Default.Equals(current, baseType))
return true;
current = current.BaseType;
}
return false;
}
/// <summary>
/// 检查类型是否实现了指定接口
/// </summary>
private bool ImplementsInterface(ITypeSymbol type, ITypeSymbol interfaceType)
{
return type.AllInterfaces.Any(i =>
SymbolEqualityComparer.Default.Equals(i, interfaceType));
}
/// <summary>
/// 查找公共基类
/// </summary>
private INamedTypeSymbol FindCommonBaseType(
ITypeSymbol type1,
ITypeSymbol type2)
{
var baseTypes1 = new HashSet<INamedTypeSymbol>(
GetBaseTypes(type1),
SymbolEqualityComparer.Default);
var current = type2.BaseType;
while (current != null)
{
if (baseTypes1.Contains(current))
return current;
current = current.BaseType;
}
// 如果没有找到,返回 System.Object
return _compilation.GetSpecialType(SpecialType.System_Object);
}
/// <summary>
/// 获取类型的所有基类
/// </summary>
private List<INamedTypeSymbol> GetBaseTypes(ITypeSymbol type)
{
var baseTypes = new List<INamedTypeSymbol>();
var current = type.BaseType;
while (current != null &&
current.SpecialType != SpecialType.System_Object)
{
baseTypes.Add(current);
current = current.BaseType;
}
return baseTypes;
}
}
public class TypeRelationship
{
public string Type1 { get; set; }
public string Type2 { get; set; }
public bool AreEqual { get; set; }
public bool Type1InheritsFromType2 { get; set; }
public bool Type2InheritsFromType1 { get; set; }
public bool Type1ImplementsType2 { get; set; }
public bool Type2ImplementsType1 { get; set; }
public bool CanConvertType1ToType2Implicitly { get; set; }
public bool CanConvertType1ToType2Explicitly { get; set; }
public bool CanConvertType2ToType1Implicitly { get; set; }
public bool CanConvertType2ToType1Explicitly { get; set; }
public string CommonBaseType { get; set; }
}最佳实践
✅ 推荐做法
csharp
/// <summary>
/// 符号比较的最佳实践
/// </summary>
public class ComparisonBestPractices
{
// ✅ 1. 使用 SymbolEqualityComparer
public bool CompareSymbols(ISymbol symbol1, ISymbol symbol2)
{
// ✅ 正确
return SymbolEqualityComparer.Default.Equals(symbol1, symbol2);
// ❌ 错误:不要使用 ==
// return symbol1 == symbol2;
}
// ✅ 2. 在集合中使用比较器
public void UseInCollections()
{
// ✅ 正确:指定比较器
var set = new HashSet<ISymbol>(SymbolEqualityComparer.Default);
var dict = new Dictionary<ISymbol, string>(SymbolEqualityComparer.Default);
}
// ✅ 3. 比较泛型类型定义
public bool IsSameGenericType(
INamedTypeSymbol type1,
INamedTypeSymbol type2)
{
// ✅ 正确:比较原始定义
return SymbolEqualityComparer.Default.Equals(
type1.ConstructedFrom,
type2.ConstructedFrom);
}
// ✅ 4. 检查继承关系
public bool InheritsFrom(ITypeSymbol type, ITypeSymbol baseType)
{
// ✅ 正确:遍历继承链
var current = type;
while (current != null)
{
if (SymbolEqualityComparer.Default.Equals(current, baseType))
return true;
current = current.BaseType;
}
return false;
}
// ✅ 5. 使用 Compilation 检查类型转换
public bool CanConvert(
Compilation compilation,
ITypeSymbol sourceType,
ITypeSymbol targetType)
{
// ✅ 正确:使用 ClassifyConversion
var conversion = compilation.ClassifyConversion(sourceType, targetType);
return conversion.IsImplicit;
}
}❌ 应避免的做法
csharp
/// <summary>
/// 应该避免的反模式
/// </summary>
public class ComparisonAntiPatterns
{
// ❌ 1. 使用 == 比较符号
public bool WrongComparison(ISymbol symbol1, ISymbol symbol2)
{
// ❌ 错误:符号实例可能不同
return symbol1 == symbol2;
// ✅ 正确
// return SymbolEqualityComparer.Default.Equals(symbol1, symbol2);
}
// ❌ 2. 使用字符串比较类型
public bool WrongTypeComparison(ITypeSymbol type)
{
// ❌ 错误:不可靠
return type.ToDisplayString() == "System.String";
// ✅ 正确
// return type.SpecialType == SpecialType.System_String;
}
// ❌ 3. 不使用比较器的集合
public void WrongCollectionUsage()
{
// ❌ 错误:没有指定比较器
var set = new HashSet<ISymbol>();
// ✅ 正确
// var set = new HashSet<ISymbol>(SymbolEqualityComparer.Default);
}
// ❌ 4. 忽略泛型类型参数
public bool WrongGenericComparison(
INamedTypeSymbol type1,
INamedTypeSymbol type2)
{
// ❌ 错误:List<int> 和 List<string> 会被认为不同
return SymbolEqualityComparer.Default.Equals(type1, type2);
// ✅ 正确:如果只想比较泛型定义
// return SymbolEqualityComparer.Default.Equals(
// type1.ConstructedFrom,
// type2.ConstructedFrom);
}
}🔑 关键要点
- 符号比较: 必须使用
SymbolEqualityComparer,不能使用== - 集合使用: 在 HashSet 和 Dictionary 中指定比较器
- 泛型类型: 使用
ConstructedFrom比较泛型定义 - 继承检查: 遍历
BaseType链检查继承关系 - 类型转换: 使用
Compilation.ClassifyConversion分析转换
📖 相关文档
- 返回索引 - 语义模型 API 概览
- INamedTypeSymbol - 类型符号详解
- 符号查找和导航 - 符号查找方法
🚀 下一步
最后更新: 2026-02-05