为什么在Hashset或其他集合中使用继承对象时,Equals(object)会胜过Equals(T)?

Mar*_*wie 20 c# inheritance iequatable

我知道我总是必须覆盖Equals(object)GetHashCode()实施时IEquatable<T>.Equals(T).

但是,我不明白,为什么在某些情况下Equals(object)胜过通用Equals(T).

例如,为什么会发生以下情况?如果我声明IEquatable<T>一个接口并X为它实现一个具体的类型,那么在将这些类型的项目相互比较时,将Equals(object)调用general Hashset<X>.在其中至少一个边被强制转换为接口的所有其他情况下,Equals(T)调用正确的.

这是一个代码示例来演示:

public interface IPerson : IEquatable<IPerson> { }

//Simple example implementation of Equals (returns always true)
class Person : IPerson
{
    public bool Equals(IPerson other)
    {
        return true;
    }

    public override bool Equals(object obj)
    {
        return true;
    }

    public override int GetHashCode()
    {
        return 0;
    }
}

private static void doEqualityCompares()
{
    var t1 = new Person();

    var hst = new HashSet<Person>();
    var hsi = new HashSet<IPerson>();

    hst.Add(t1);
    hsi.Add(t1);

    //Direct comparison
    t1.Equals(t1);                  //IEquatable<T>.Equals(T)

    hst.Contains(t1);               //Equals(object) --> why? both sides inherit of IPerson...
    hst.Contains((IPerson)t1);      //IEquatable<T>.Equals(T)

    hsi.Contains(t1);               //IEquatable<T>.Equals(T)
    hsi.Contains((IPerson)t1);      //IEquatable<T>.Equals(T)
}
Run Code Online (Sandbox Code Playgroud)

Ser*_*rvy 21

HashSet<T>EqualityComparer<T>.Default在没有提供比较器时调用以获取默认的相等比较器.

EqualityComparer<T>.Default确定是否T实施IEquatable<T>.如果是,它使用它,如果不是,它使用object.Equalsobject.GetHashCode.

你的Person对象IEquatable<IPerson>没有实现IEquatable<Person>.

当你有一个HashSet<Person>它最终检查是否Person是一个IEquatable<Person>,它不是,所以它使用object方法.

当你HashSet<IPerson>检查它是否IPerson是一个IEquatable<IPerson>,它是什么,所以它使用这些方法.


至于其余的情况,为什么这行:

hst.Contains((IPerson)t1);
Run Code Online (Sandbox Code Playgroud)

调用IEquatable Equals方法,即使它调用了HashSet<Person>.在这里你调用Contains一个HashSet<Person>和传递的IPerson. HashSet<Person>.Contains要求参数为a Person; 一个IPerson不是一个有效的论点.但是,a HashSet<Person>也是一个IEnumerable<Person>,并且由于IEnumerable<T>是协变的,这意味着它可以被视为一个IEnumerable<IPerson>,它有一个Contains扩展方法(通过LINQ)接受IPerson一个参数.

IEnumerable.ContainsEqualityComparer<T>.Default当没有提供时,它也用于获得它的相等比较器.在这个方法调用的情况下,我们实际上正在调用Contains一个IEnumerable<IPerson>,这意味着EqualityComparer<IPerson>.Default检查是否IPerson是一个IEquatable<IPerson>,它是什么,以便Equals调用该方法.