repetidos objetos funciona elementos ejemplos ejemplo como coleccion c# inheritance iequatable

c# - objetos - ¿Por qué gana Equals(objeto) sobre Equals(T) al usar un objeto heredado en Hashset u otras colecciones?



hashset java elementos repetidos (2)

Soy consciente del hecho de que siempre tengo que anular Equals(object) y GetHashCode() al implementar IEquatable<T>.Equals(T) .

Sin embargo, no entiendo, por qué en algunas situaciones los Equals(object) ganan sobre los Equals(T) genéricos Equals(T) .

Por ejemplo, ¿por qué sucede lo siguiente? Si declaro IEquatable<T> para una interfaz e implemento un tipo concreto X para él, el Hashset<X> llama al general Equals(object) cuando compara elementos de ese tipo entre sí. En todas las demás situaciones en las que al menos uno de los lados se convierte en la Interfaz, se llama a los Equals(T) correctos Equals(T) .

Aquí hay un ejemplo de código para demostrar:

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) }


Aunque IComparable<in T> es contravariante con respecto a T , de modo que cualquier tipo que implemente IComparable<Person> se consideraría automáticamente una implementación de IComparable<IPerson> , el tipo IEquatable<T> está diseñado para su uso con tipos sellados, especialmente estructuras El requisito de que Object.GetHashCode() sea ​​coherente tanto con IEquatable<T>.Equals(T) como con Object.Equals(Object) generalmente implica que los dos últimos métodos deberían comportarse de manera idéntica, lo que a su vez implica que uno de ellos debe encadenarse a el otro. Si bien hay una gran diferencia de rendimiento entre pasar una estructura directamente a una IEquatable<T> del tipo adecuado, en comparación con construir una instancia del tipo de objeto de caja en caja de la estructura y tener una implementación de Equals(Object) copie los datos de la estructura fuera de eso, no existe tal rendimiento diferente con los tipos de referencia. Si IEquatable<T>.Equals(T) y Equals(Object) van a ser equivalentes y T es un tipo de referencia heredable, no hay una diferencia significativa entre:

bool Equals(MyType obj) { MyType other = obj as MyType; if (other==null || other.GetType() != typeof(this)) return false; ... test whether other matches this } bool Equals(MyType other) { if (other==null || other.GetType() != typeof(this)) return false; ... test whether other matches this }

Este último podría guardar un encasillado, pero es poco probable que exista una diferencia de rendimiento suficiente para justificar tener dos métodos.


HashSet<T> llama a EqualityComparer<T>.Default para obtener el comparador de igualdad predeterminado cuando no se proporciona ningún comparador.

EqualityComparer<T>.Default determina si T implementa IEquatable<T> . Si lo hace, usa eso, si no, usa object.Equals y object.GetHashCode .

Su objeto Person implementa IEquatable<IPerson> no IEquatable<Person> .

Cuando tienes un HashSet<Person> , termina verificando si Person es un IEquatable<Person> que es IEquatable<Person> , que no, por lo que utiliza los métodos de object .

Cuando tienes un HashSet<IPerson> , comprueba si IPerson es un IEquatable<IPerson> , que es, por lo que utiliza esos métodos.

En cuanto al caso restante, ¿por qué la línea:

hst.Contains((IPerson)t1);

llame al método IEquatable Equals aunque se llame en HashSet<Person> . Aquí está llamando a Contains en un HashSet<Person> y está pasando un IPerson . HashSet<Person>.Contains requiere que el parámetro sea una Person ; Un IPerson no es un argumento válido. Sin embargo, un HashSet<Person> también es un IEnumerable<Person> , y dado que IEnumerable<T> es covariante, eso significa que se puede tratar como un IEnumerable<IPerson> , que tiene un método de extensión Contains (a través de LINQ) que acepta un IPerson como parámetro.

IEnumerable.Contains también usa EqualityComparer<T>.Default para obtener su comparador de igualdad cuando no se proporciona ninguno. En el caso de esta llamada de método, en realidad estamos llamando a Contains en un IEnumerable<IPerson> , lo que significa que EqualityComparer<IPerson>.Default está verificando si IPerson es un IEquatable<IPerson> , que es, por lo que el método de Equals llamado.