Quiero entender los escenarios donde IEqualityComparer<T>
y IEquatable<T>
deberían usarse. La documentación de MSDN para ambos se ve muy similar.
151
Quiero entender los escenarios donde IEqualityComparer<T>
y IEquatable<T>
deberían usarse. La documentación de MSDN para ambos se ve muy similar.
EqualityComparer<T>
lugar de implementar la interfaz "porqueEqualityComparer<T>
prueba la igualdad usandoIEquatable<T>
T
implementaciónIEquatable<T>
. ¿DeList<T>
lo contrario, una colección tiene algún tipo de error sutil?Respuestas:
IEqualityComparer<T>
es una interfaz para un objeto que realiza la comparación en dos objetos del tipoT
.IEquatable<T>
es para un objeto de tipoT
para que pueda compararse con otro del mismo tipo.fuente
IEqualityComparer<T>
es una interfaz para un objeto (que normalmente es una diferente clase de peso ligero deT
) que proporciona funciones de comparación que opera enT
Al decidir si usar
IEquatable<T>
oIEqualityComparer<T>
, uno podría preguntar:Si solo hay una forma de probar dos instancias de
T
igualdad, o si se prefiere uno de varios métodos, entoncesIEquatable<T>
sería la elección correcta: se supone que esta interfaz se implementa solo porT
sí misma, 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 de comparar dos
T
s para la igualdad,IEqualityComparer<T>
parecería más apropiado: esta interfaz no debe implementarse porT
sí misma, sino por otras clases "externas". Por lo tanto, al probar dos instancias deT
igualdad, ya queT
no tiene una comprensión interna de la igualdad, tendrá que hacer una elección explícita de unaIEqualityComparer<T>
instancia que realice la prueba de acuerdo con sus requisitos específicos.Ejemplo:
Consideremos estos dos tipos (que se supone que tienen una semántica de valor ):
¿Por qué solo uno de estos tipos heredaría
IEquatable<>
, pero no el otro?En teoría, solo hay una manera sensata de comparar dos instancias de cualquier tipo: son iguales si las propiedades
X
yY
en ambas instancias son iguales. De acuerdo con este pensamiento, ambos tipos deberían implementarseIEquatable<>
, porque no parece probable que haya otras formas significativas de hacer una prueba de igualdad.El problema aquí es que comparar números de coma flotante para la igualdad podría no funcionar como se esperaba, debido a errores de redondeo minucioso . Existen diferentes métodos para comparar números de punto flotante para casi la igualdad , cada uno con ventajas y compensaciones específicas, y es posible que desee poder elegir qué método es el adecuado.
Tenga en cuenta que la página a la que me vinculé (arriba) declara explícitamente que esta prueba de casi igualdad tiene algunas debilidades. Como se trata de una
IEqualityComparer<T>
implementación, simplemente puede cambiarla si no es lo suficientemente buena para sus propósitos.fuente
IEqualityComparer<T>
implementación legítima debe implementar una relación de equivalencia, lo que implica que en todos los casos en que dos objetos se comparan igual a un tercero, deben compararse entre sí. Cualquier clase que implementeIEquatable<T>
oIEqualityComparer<T>
de una manera contraria a la anterior está rota.IEqualityComparer<String>
que considera "hola" igual a "Hola" y "hola" debe considerar "hola" y "hola" iguales entre sí, pero para la mayoría de los métodos de comparación eso no sería un problema.GetHashCode
debería permitirle determinar rápidamente si dos valores difieren. Las reglas son aproximadamente las siguientes: (1)GetHashCode
siempre debe producir el mismo código hash para el mismo valor. (2)GetHashCode
debe ser rápido (más rápido queEquals
). (3)GetHashCode
no tiene que ser preciso (no tan preciso comoEquals
). Esto significa que puede producir el mismo código hash para diferentes valores. Cuanto más preciso sea, mejor, pero probablemente sea más importante mantenerlo rápido.Ya tienes la definición básica de lo que son . En resumen, si implementa
IEquatable<T>
en claseT
, elEquals
método en un objeto de tipoT
le dice si el objeto mismo (el que se está probando para la igualdad) es igual a otra instancia del mismo tipoT
. Mientras que,IEqualityComparer<T>
es para probar la igualdad de dos instancias deT
, típicamente fuera del alcance de las instancias deT
.En cuanto a lo que son, puede ser confuso al principio. A partir de la definición, debe quedar claro que, por lo tanto,
IEquatable<T>
(definido en laT
propia clase ) debería ser el estándar de facto para representar la unicidad de sus objetos / instancias.HashSet<T>
,Dictionary<T, U>
(teniendo enGetHashCode
cuenta que también se anula),Contains
enList<T>
etc. haga uso de esto. ImplementarIEqualityComparer<T>
enT
no ayuda a los casos generales mencionados anteriormente. Posteriormente, hay poco valor para implementarIEquatable<T>
en cualquier otra clase que no seaT
. Esta:Rara vez tiene sentido.
Por otra parte
así es como debe 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 dePerson
en algún momento, es posible que deba probar la igualdad de dos personas en función de su edad. En ese caso puedes hacer:Para probarlos, intente
Del mismo modo
IEqualityComparer<T>
enT
que no tiene sentido.Es cierto que esto funciona, pero no se ve bien a los ojos y derrota la lógica.
Por lo general, lo que necesitas es
IEquatable<T>
. También, idealmente, puede tener solo uno,IEquatable<T>
mientras que múltiplesIEqualityComparer<T>
es posible según diferentes criterios.Los
IEqualityComparer<T>
yIEquatable<T>
son exactamente análogos aComparer<T>
yIComparable<T>
que se utilizan para fines de comparación en lugar de igualar; un buen hilo aquí donde escribí la misma respuesta :)fuente
public int GetHashCode(Person obj)
debería volverobj.GetHashCode()
obj.Age.GetHashCode
. editaráperson.GetHashCode
ninguna parte ... ¿por qué anularía eso? - Anulamos porque el objetivoIEqualityComparer
es tener una implementación de comparación diferente de acuerdo con nuestras reglas, las reglas que realmente conocemos bien.Age
propiedad, así que llamoAge.GetHashCode
. La edad es de tipoint
, pero cualquiera que sea el tipo de contrato de código hash en .NET es esodifferent objects can have equal hash but equal objects cant ever have different hashes
. Este es un contrato que podemos creer ciegamente. Claro, nuestros comparadores personalizados también deben cumplir con esta regla. Si alguna vez llamarsomeObject.GetHashCode
rompe cosas, entonces es el problema con los implementadores de tiposomeObject
, no es nuestro problema.IEqualityComparer se utiliza cuando la igualdad de dos objetos se implementa externamente, por ejemplo, si desea definir un comparador para dos tipos para los que no tenía la fuente, o para los casos en que 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.
fuente
Uno compara dos
T
s. El otro puede compararse con otrosT
s. Por lo general, solo necesitará usar uno a la vez, no ambos.fuente