Skip to content

符号比较和等价性

深入理解 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);
    }
}

🔑 关键要点

  1. 符号比较: 必须使用 SymbolEqualityComparer,不能使用 ==
  2. 集合使用: 在 HashSet 和 Dictionary 中指定比较器
  3. 泛型类型: 使用 ConstructedFrom 比较泛型定义
  4. 继承检查: 遍历 BaseType 链检查继承关系
  5. 类型转换: 使用 Compilation.ClassifyConversion 分析转换

📖 相关文档


🚀 下一步


最后更新: 2026-02-05

基于 MIT 许可发布