2012-02-16 15 views
113

Quiero entender los escenarios en los que se deben usar IEqualityComparer<T> y IEquatable<T>. La documentación de MSDN para ambos se ve muy similar.¿Cuál es la diferencia entre IEqualityComparer <T> e IEquatable <T>?

+3

¡esta pregunta necesita más votos positivos! – nawfal

+1

[MSDN sez:] (https://msdn.microsoft.com/en-us/library/ms132151 (v = vs.110) .aspx) "Esta interfaz permite la implementación de la comparación de igualdad personalizada ** para colecciones. * * "que por supuesto se infiere en la respuesta seleccionada. MSDN también recomienda heredar 'EqualityComparer ' en lugar de implementar la interfaz "porque' EqualityComparer 'prueba la igualdad usando' IEquatable ' – radarbob

+0

... lo anterior sugiere que debo crear una colección personalizada para cualquier' T' que implemente 'IEquatable '. ¿Una colección como 'List ' tiene algún tipo de error sutil? – radarbob

Respuesta

94

IEqualityComparer<T> es una interfaz para un objeto que realiza la comparación en dos objetos del tipo T.

IEquatable<T> es para un objeto del tipo T para que pueda compararse con otro.

+34

+1 _IEquatable es para un objeto para que se pueda comparar con otro_ ** del mismo tipo ** – gdoron

+0

Simple, conciso y fácil de comprender – Tilak

+0

Ditto @Tilak .... No se necesita más explicación – rajibdotnet

3

Uno compara dos T s. El otro puede compararse con otros T s. Usualmente, solo necesitarás usar uno a la vez, no ambos.

9

IEqualityComparer es para usar cuando la igualdad de dos objetos se implementa externamente, p. si quería definir un comparador para dos tipos para los que no tenía la fuente, o para casos donde la igualdad entre dos cosas solo tiene sentido en un contexto limitado.

IEtabletable es para el objeto en sí (el que se compara por la igualdad) para implementar.

+0

Usted es el único que planteó realmente el problema "Plugging in Equality" (uso externo). Más 1. –

16

Ya tiene la definición básica de que son. En resumen, si implementa IEquatable<T> en la clase T, el método Equals en un objeto de tipo T le indica si el objeto en sí (el que se prueba para la igualdad) es igual a otra instancia del mismo tipo T. Mientras que, IEqualityComparer<T> es para probar la igualdad de dos instancias de T, generalmente fuera del alcance de las instancias de T.

En cuanto a lo que son para puede ser confuso al principio. A partir de la definición, debe quedar claro que, por lo tanto, IEquatable<T> (definido en la clase T en sí misma) debe ser el estándar de facto para representar la singularidad de sus objetos/instancias. HashSet<T>, Dictionary<T, U> (teniendo en cuenta GetHashCode se reemplaza también), Contains en List<T> etc. hacer uso de esto. Implementar IEqualityComparer<T> en T no ayuda en los casos generales mencionados anteriormente. Posteriormente, hay poco valor para implementar IEquatable<T> en cualquier otra clase que no sea T. Esto:

class MyClass : IEquatable<T> 

rara vez tiene sentido.

Por otro lado

class T : IEquatable<T> 
{ 
    //override ==, !=, GetHashCode and non generic Equals as well 

    public bool Equals(T other) 
    { 
     //.... 
    } 
} 

es como debe hacerse.

IEqualityComparer<T> puede ser útil cuando necesita una validación de igualdad personalizada, pero no como una regla general. Por ejemplo, en una clase de Person en algún momento, es posible que deba probar la igualdad de dos personas en función de su edad.En ese caso se puede hacer:

class Person 
{ 
    public int Age; 
} 

class AgeEqualityTester : IEqualityComparer<Person> 
{ 
    public bool Equals(Person x, Person y) 
    { 
     return x.Age == y.Age; 
    } 

    public int GetHashCode(Person obj) 
    { 
     return obj.Age.GetHashCode; 
    } 
} 

Para probarlos, tratar

var people = new Person[] { new Person { age = 23 } }; 
Person p = new Person() { age = 23 }; 

print people.Contains(p); //false; 
print people.Contains(p, new AgeEqualityTester()); //true 

Del mismo modo IEqualityComparer<T> en T no tiene sentido.

class Person : IEqualityComparer<Person> 

Verdadero esto funciona, pero no se ve bien a los ojos y derrota la lógica.

Normalmente lo que necesita es IEquatable<T>. También idealmente puede tener solo un IEquatable<T>, mientras que es posible un múltiplo IEqualityComparer<T> según diferentes criterios.

El IEqualityComparer<T> y IEquatable<T> son exactamente análoga a Comparer<T> y IComparable<T> que se utilizan para propósitos de comparación en lugar de equiparar; un buen hilo here donde escribí la misma respuesta :)

+0

'public int GetHashCode (Person obj)' debe devolver 'obj.GetHashCode()' –

+0

@HristoYankov debe devolver 'obj.Age.GetHashCode'. editará – nawfal

+0

Por favor, explique por qué el comparador debe hacer tal suposición acerca de cuál es el hash del objeto que compara? Su comparador no sabe cómo Person calcula su Hash, ¿por qué anularía eso? –

36

la hora de decidir si se debe utilizar o IEquatable<T>IEqualityComparer<T>, uno podría preguntar:

¿Hay una forma preferida de poner a prueba dos instancias de T por la igualdad, o hay varias formas igualmente válidas?

  • Si sólo hay una manera de probar dos instancias de T por la igualdad, o si se prefiere uno de varios métodos, a continuación, IEquatable<T> sería la opción correcta: se supone Esta interfaz para ser implementado solamente por T en sí, de modo que una instancia de T tiene conocimiento interno de cómo compararse con otra instancia de T.

  • Por otro lado, si hay varios métodos igualmente razonables de la comparación de dos T s para la igualdad, IEqualityComparer<T> parece más adecuado: Esta interfaz no está destinado a ser implementado por T en sí, sino por otras clases "externos" . Por lo tanto, al probar dos instancias de T para la igualdad, porque T no tiene una comprensión interna de la igualdad, tendrá que hacer una elección explícita de una instancia IEqualityComparer<T> que realice la prueba de acuerdo con sus requisitos específicos.

Ejemplo:

Vamos a considerar estos dos tipos (que se supone que tienen value semantics):

interface IIntPoint : IEquatable<IIntPoint> 
{ 
    int X { get; } 
    int Y { get; } 
} 

interface IDoublePoint // does not inherit IEquatable<IDoublePoint>; see below. 
{ 
    double X { get; } 
    double Y { get; } 
} 

¿Por qué sólo uno de estos tipos heredar IEquatable<>, pero no el otro ?

En teoría, solo hay una forma sensata de comparar dos instancias de cualquier tipo: Son iguales si las propiedades X y Y en ambas instancias son iguales.Según este pensamiento, ambos tipos deberían implementar IEquatable<>, porque no parece probable que existan otras formas significativas de hacer una prueba de igualdad.

El problema aquí es que comparing floating-point numbers for equality might not work as expected, due to minute rounding errors. Existen diferentes métodos para comparar números de coma flotante para near-equality, cada uno con ventajas específicas y compensaciones, y es posible que desee poder elegir qué método es el adecuado.

sealed class DoublePointNearEqualityComparerByTolerance : IEqualityComparer<IDoublePoint> 
{ 
    public DoublePointNearEqualityComparerByTolerance(double tolerance) { … } 
    … 
    public bool Equals(IDoublePoint a, IDoublePoint b) 
    { 
     return Math.Abs(a.X - b.X) <= tolerance && Math.Abs(a.Y - b.Y) <= tolerance; 
    } 
    … 
} 

Tenga en cuenta que la página a la que he vinculado (arriba) establece explícitamente que esta prueba para near-equality tiene algunos puntos débiles. Como se trata de una implementación IEqualityComparer<T>, simplemente puede cambiarla si no es lo suficientemente buena para sus propósitos.

+4

El método de comparación sugerido para probar la igualdad de doble punto está roto, ya que cualquier implementación legítima de 'IEqualityComparer '* debe * implementar una relación de equivalencia, lo que implica que en * todos * casos donde dos objetos se comparan igual a un tercero, * deben * compare igual el uno al otro. Cualquier clase que implemente 'IEtabletable ' o 'IEqualityComparer ' de forma contraria a lo anterior se rompe. – supercat

+3

Un mejor ejemplo serían las comparaciones entre cadenas. Tiene sentido tener un método de comparación de cadenas que considere las cadenas como iguales solo si contienen la misma secuencia de bytes, pero hay otros métodos de comparación útiles * que también constituyen relaciones de equivalencia *, como las comparaciones insensibles a mayúsculas y minúsculas.Tenga en cuenta que un 'IEqualityComparer ' que considere "hello" igual a "Hello" y "hElLo" debe considerar "Hello" y "hElLo" iguales entre sí, pero para la mayoría de los métodos de comparación eso no sería un problema. – supercat

+0

@supercat, gracias por los valiosos comentarios. Es probable que no repase la revisión de mi respuesta en los próximos días, así que, si lo desea, puede editar como lo considere oportuno. – stakx

Cuestiones relacionadas