2008-09-16 17 views
6

La implementación de Igual() para los tipos de referencia es más difícil de lo que parece. Mi aplicación canónica actual es la siguiente:¿Cuál es la "mejor" implementación canónica de Equals() para los tipos de referencia?

public bool Equals(MyClass obj) 
{ 
    // If both refer to the same reference they are equal. 
    if(ReferenceEquals(obj, this)) 
    return true; 

    // If the other object is null they are not equal because in C# this cannot be null. 
    if(ReferenceEquals(obj, null)) 
    return false; 

    // Compare data to evaluate equality  
    return _data.Equals(obj._data); 
} 

public override bool Equals(object obj) 
{ 
    // If both refer to the same reference they are equal. 
    if(ReferenceEquals(obj, this)) 
    return true; 

    // If the other object is null or is of a different types the objects are not equal. 
    if(ReferenceEquals(obj, null) || obj.GetType() != GetType()) 
    return false; 

    // Use type-safe equality comparison 
    return Equals((MyClass)obj); 
} 

public override int GetHashCode() 
{ 
    // Use data's hash code as our hashcode 
    return _data.GetHashCode(); 
} 

creo que esto cubre toda la esquina (herencia y tales casos), pero puedo estar equivocado. ¿Qué piensan ustedes?

Respuesta

4

Escribí una guía bastante completa de esto hace un tiempo. Para empezar, las implementaciones iguales deben compartirse (es decir, la sobrecarga que toma un objeto debe pasar a la que toma un objeto fuertemente tipado). Además, debe considerar cosas tales como que su objeto debe ser inmutable debido a la necesidad de anular GetHashCode. Más información aquí:

http://gregbeech.com/blog/implementing-object-equality-in-dotnet

+0

Mi implementación es "compartida". Como puede ver, al final de Equals (Object) hay una llamada a Equals (MyClass). Soy consciente de los problemas de mutabilidad y GetHashCode(); pero es una observación importante. Me ha mordido varias veces. Lástima que no hay una forma "fácil" de declarar solo "clases" de solo lectura. –

+0

El enlace está roto por ahora. – Restuta

0

En cuanto a la herencia, creo que debería dejar que el paradigma OO haga su magia.

En concreto, se debe eliminar la comprobación GetType(), ya que podría romper el polimorfismo más adelante.

+1

No estoy de acuerdo. Imagine que tenemos clases de Apple y Orange que derivan de la clase Fruit. Si eliminamos la comprobación de GetType() en la implementación de Equals in Fruit, podríamos comparar manzanas con naranjas a menos que todas las clases derivadas también anulen correctamente Igual(). Podría desordenarse muy rápido. –

0

Estoy de acuerdo con chakrit, los objetos de diferentes tipos deben ser semánticamente iguales si tienen los mismos datos o ID.

Personalmente, yo uso el siguiente:

public override bool Equals(object obj) 
    { 
     var other = obj as MyClass; 
     if (other == null) return false; 

     return this.data.Equals(other.data); 
    } 
+0

No estoy de acuerdo con usted. Esto nos permitiría comparar manzanas con naranjas. –

+0

El punto es que tal vez usted quiere que sean iguales. La verificación GetType no permitirá el uso de proxies, por ejemplo; tendrá 2 objetos idénticos, uno envuelto dentro de un contenedor de un tipo diferente, pero desea identificarlos como iguales. –

+0

@SantiagoPalladino: A menudo tiene sentido definir comparadores de igualdad que usan una definición de equivalencia más flexible que 'Object.Equals'. Por ejemplo, .net define varios comparadores de cadenas insensibles a mayúsculas y minúsculas que considerarían "HELLO" como equivalente a "Hola" o "heLlO", aunque las cadenas son diferentes. Tales comparadores podrían aceptar como objetos iguales a diferentes tipos. Me disgusta mucho la noción de que los objetos reemplacen a 'Object.Equals' para definir una relación de equivalencia que sea tan suelta que permita que los objetos de diferentes tipos se puedan comparar desiguales, a menos que se proporcione explícitamente ... – supercat

1

mejor esperanza de que this._data no es nulo si también es un tipo de referencia.

public bool Equals(MyClass obj) 
{ 
    if (obj == null) { 
     return false; 
    } 
    else { 
     return (this._data != null && this._data.Equals(obj._data)) 
         || obj._data == null; 
    } 
} 

public override bool Equals(object obj) 
{ 
    if (obj == null || !(obj is MyClass)) { 
     return false; 
    } 
    else { 
     return this.Equals((MyClass)obj); 
    } 
} 

public override int GetHashCode() { 
    return this._data == null ? 0 : this._data.GetHashCode(); 
} 
+0

Tienes razón. Esta es solo una implementación "canónica" para demostrar los conceptos detrás de "Iguales". Mi implementación "real" generalmente se implementa utilizando Equals (a, b) en lugar de a.Equals (b). –

Cuestiones relacionadas