考虑以下代码:

    class MyType : TypeDelegator
    {
       public MyType(Type parent)
          : base(parent)
       {
       }
    }

    class Program
    {
       static void Main(string[] args)
       {
          Type t1 = typeof(string);
          Type t2 = new MyType(typeof(string));

          Console.WriteLine(EqualityComparer<Type>.Default.Equals(t1, t2)); // <-- false
          Console.WriteLine(EqualityComparer<Type>.Default.Equals(t2, t1)); // <-- true

          Console.WriteLine(t1.Equals(t2)); // <-- true
          Console.WriteLine(t2.Equals(t1)); // <-- true

          Console.WriteLine(Object.Equals(t1, t2)); // <-- false
          Console.WriteLine(Object.Equals(t2, t1)); // <-- true
       }
   }

各种版本的Equals如何返回不同的结果? EqualityComparer.Default可能调用Object.Equals,因此这些结果匹配,尽管它们本身不一致。并且Equals的普通实例版本都返回true

当让一个方法返回实际上继承自TypeTypeDelegator时,这显然会产生问题。想象一下,例如将这些类型作为键放在字典中,默认情况下,使用EqualityComparer.Default进行比较。

有什么办法解决这个问题?我希望上面代码中的所有方法都返回true

最佳答案

更新

该答案的代码已成为GitHub上的存储库:Undefault.NET on GitHub

史蒂文(Steven)很好地解释了为什么这样做会起作用。我不认为Object.Equals案例有解决方案。然而,

我找到了一种方法,通过使用反射配置默认的相等比较器来解决EqualityComparer<T>.Default情况下的问题。

每个应用程序生命周期仅需要发生一次这种小小的黑客攻击。启动将是执行此操作的好时机。使它起作用的代码行是:

DefaultComparisonConfigurator.ConfigureEqualityComparer<Type>(new HackedTypeEqualityComparer());

执行该代码后,EqualityComparer<Type>.Default.Equals(t2, t1))将产生与EqualityComparer<Type>.Default.Equals(t1,t2))相同的结果(在您的示例中)。

支持的基础结构代码包括:

1.自定义IEqualityComparer<Type>实现

此类按照您希望其行为的方式处理相等性比较。
public class HackedTypeEqualityComparer : EqualityComparer<Type> {

    public override bool Equals(Type one, Type other){
        return ReferenceEquals(one,null)
            ? ReferenceEquals(other,null)
            : !ReferenceEquals(other,null)
                && ( (one is TypeDelegator || !(other is TypeDelegator))
                    ? one.Equals(other)
                    : other.Equals(one));
    }

    public override int GetHashCode(Type type){ return type.GetHashCode(); }

}

2. Configurator类

此类使用反射来配置EqualityComparer<T>.Default的基础字段。另外,此类提供了一种机制来操纵Comparer<T>.Default的值,并确保所配置的实现的结果兼容。还有一种方法可以将配置恢复为框架默认值。
public class DefaultComparisonConfigurator
{

    static DefaultComparisonConfigurator(){
        Gate = new object();
        ConfiguredEqualityComparerTypes = new HashSet<Type>();
    }

    private static readonly object Gate;
    private static readonly ISet<Type> ConfiguredEqualityComparerTypes;

    public static void ConfigureEqualityComparer<T>(IEqualityComparer<T> equalityComparer){
        if(equalityComparer == null) throw new ArgumentNullException("equalityComparer");
        if(EqualityComparer<T>.Default == equalityComparer) return;
        lock(Gate){
            ConfiguredEqualityComparerTypes.Add(typeof(T));
            FieldFor<T>.EqualityComparer.SetValue(null,equalityComparer);
            FieldFor<T>.Comparer.SetValue(null,new EqualityComparerCompatibleComparerDecorator<T>(Comparer<T>.Default,equalityComparer));
        }
    }

    public static void ConfigureComparer<T>(IComparer<T> comparer){
        if(comparer == null) throw new ArgumentNullException("comparer");
        if(Comparer<T>.Default == comparer) return;
        lock(Gate){
            if(ConfiguredEqualityComparerTypes.Contains(typeof(T)))
                FieldFor<T>.Comparer.SetValue(null,new EqualityComparerCompatibleComparerDecorator<T>(comparer,EqualityComparer<T>.Default));
            else
                FieldFor<T>.Comparer.SetValue(null,comparer);
        }
    }

    public static void RevertConfigurationFor<T>(){
        lock(Gate){
            FieldFor<T>.EqualityComparer.SetValue(null,null);
            FieldFor<T>.Comparer.SetValue(null,null);
            ConfiguredEqualityComparerTypes.Remove(typeof(T));
        }
    }

    private static class FieldFor<T> {

        private const string FieldName = "defaultComparer";
        private const BindingFlags FieldBindingFlags = BindingFlags.NonPublic|BindingFlags.Static;

        static FieldInfo comparer, equalityComparer;

        public static FieldInfo Comparer { get { return comparer ?? (comparer = typeof(Comparer<T>).GetField(FieldName,FieldBindingFlags)); } }

        public static FieldInfo EqualityComparer { get { return equalityComparer ?? (equalityComparer = typeof(EqualityComparer<T>).GetField(FieldName,FieldBindingFlags)); } }

    }
}

3.兼容的IComparer<T>实现

基本上,这是IComparer<T>的装饰器,可确保在注入(inject)Comparer<T>EqualityComparer<T>EqualityComparer<T>之间具有兼容性。确保已配置的IEqualityComparer<T>实现实现认为相等的任何两个值始终具有0的比较结果。
public class EqualityComparerCompatibleComparerDecorator<T> : Comparer<T> {

    public EqualityComparerCompatibleComparerDecorator(IComparer<T> comparer, IEqualityComparer<T> equalityComparer){
        if(comparer == null) throw new ArgumentNullException("comparer");
        if(equalityComparer == null) throw new ArgumentNullException("equalityComparer");
        this.comparer = comparer;
        this.equalityComparer = equalityComparer;
    }

    private readonly IComparer<T> comparer;
    private readonly IEqualityComparer<T> equalityComparer;

    public override int Compare(T left, T right){ return this.equalityComparer.Equals(left,right) ?  0 : comparer.Compare(left,right); }

}

10-04 14:41