c# - ¿Cuál es la diferencia entre IEqualityComparer<T> e IEquatable<T>?
.net equals (5)
Al decidir si usar IEquatable<T>
o IEqualityComparer<T>
, uno podría preguntar:
¿Hay una forma preferida de probar dos instancias de
T
para la igualdad, o hay varias formas igualmente válidas?
Si solo hay una forma de probar dos instancias de
T
para igualdad, o si se prefiere uno de varios métodos,IEquatable<T>
sería la elección correcta: se supone que esta interfaz debe implementarse solo porT
, de modo que una instancia deT
tiene conocimiento interno de cómo compararse con otra instancia deT
Por otro lado, si hay varios métodos igualmente razonables para comparar dos
T
para la igualdad,IEqualityComparer<T>
parece más apropiado: Esta interfaz no debe ser implementada porT
sí, sino por otras clases "externas". Por lo tanto, al probar dos instancias deT
para igualdad, porqueT
no tiene una comprensión interna de igualdad, tendrá que hacer una elección explícita de unaIEqualityComparer<T>
deIEqualityComparer<T>
que realice la prueba de acuerdo con sus requisitos específicos.
Ejemplo:
Consideremos estos dos tipos (que se supone que tienen semántica de valores ):
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é solo uno de estos tipos heredaría 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
e Y
en ambos casos son iguales. De acuerdo con 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 la comparación de los números de coma flotante para la igualdad podría no funcionar como se esperaba, debido a los errores de redondeo por minuto . Existen diferentes métodos para comparar los números de coma flotante para la igualdad casi total , 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 de casi igualdad tiene algunas debilidades. Como se trata de una IEqualityComparer<T>
, simplemente puede IEqualityComparer<T>
si no es lo suficientemente buena para sus propósitos.
Quiero entender los escenarios donde IEqualityComparer<T>
e IEquatable<T>
. La documentación de MSDN para ambos se ve muy similar.
IEqualityComparer se utiliza cuando la igualdad de dos objetos se implementa externamente, por ejemplo, si se quiere definir un comparador para dos tipos para los que no se tiene la fuente, o para casos donde la igualdad entre dos cosas solo tiene sentido en un contexto limitado.
IEquatable es para el objeto en sí (el que se compara por igualdad) para implementar.
Uno compara dos T
s. El otro puede compararse con otras T
s. Usualmente, solo necesitarás usar uno a la vez, no ambos.
Ya tienes la definición básica de lo que son . En resumen, si implementa IEquatable<T>
en la clase T
, el método Equals
en un objeto de tipo T
le dice si el objeto mismo (el que se prueba para la igualdad) es igual a otra instancia del mismo tipo T
Considerando 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, puede ser confuso al principio. A partir de la definición, debe quedar claro que, por IEquatable<T>
tanto, IEquatable<T>
(definido en la propia clase T
) debería ser el estándar de facto para representar la singularidad de sus objetos / instancias. HashSet<T>
, Dictionary<T, U>
(teniendo en cuenta que GetHashCode
se reemplaza), Contains
on List<T>
etc. hacen uso de esto. La implementación de IEqualityComparer<T>
en T
no ayuda a los casos generales mencionados anteriormente. Posteriormente, hay poco valor para implementar IEquatable<T>
en cualquier otra clase que no sea T
Esta:
class MyClass : IEquatable<T>
raramente tiene sentido.
Por otra parte
class T : IEquatable<T>
{
//override ==, !=, GetHashCode and non generic Equals as well
public bool Equals(T other)
{
//....
}
}
es cómo debería hacerse.
IEqualityComparer<T>
puede ser útil cuando necesita una validación personalizada de igualdad, pero no como una regla general. Por ejemplo, en una clase de Person
en algún momento, puede que necesite probar la igualdad de dos personas en función de su edad. En ese caso, puedes 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, prueba
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 IEqualityComparer<T>
modo, IEqualityComparer<T>
en T
no tiene sentido.
class Person : IEqualityComparer<Person>
Es cierto que esto funciona, pero no se ve bien a los ojos y derrota la lógica.
Por lo general, lo que necesita es IEquatable<T>
. También idealmente puede tener solo un IEquatable<T>
mientras que es posible IEqualityComparer<T>
múltiples IEqualityComparer<T>
según diferentes criterios.
IEqualityComparer<T>
e IEquatable<T>
son exactamente análogos a Comparer<T>
e IComparable<T>
que se utilizan con fines comparativos en lugar de equiparar; un buen hilo here donde escribí la misma respuesta :)
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 de tipo T
para que pueda compararse con otro.