2010-04-29 14 views
114

Quiero que mi clase Food pueda probar cada vez que sea igual a otra instancia de Food. Más tarde lo usaré contra una Lista, y quiero usar su método List.Contains(). ¿Debería implementar IEquatable<Food> o simplemente anular Object.Equals()? De MSDN:¿Cuál es la diferencia entre IEquatable y simplemente anulando Object.Equals()?

Este método determina la igualdad de utilizando el comparador de igualdad predeterminado, según lo definido por la implementación del objeto del método IEquatable.Equals para T (el tipo de valores en la lista).

Así que mi siguiente pregunta es: ¿qué funciones/clases del .NET framework hacen uso de Object.Equals()? ¿Debería usarlo en primer lugar?

+2

Una muy buena explicación aquí http://blogs.msdn.com/b/jaredpar/archive/2009/01/15/if-you-implement-iequatable-t-you-still-must-override-object- s-equals-and-gethashcode.aspx – nawfal

+1

@nawfal: eso ya se mencionó en la respuesta aceptada. –

+0

posible duplicado de [Entender IEquatable] (http://stackoverflow.com/questions/411500/understanding-iequatable) – nawfal

Respuesta

140

La razón principal es el rendimiento. Cuando se introdujeron los genéricos en .NET 2.0, pudieron agregar un montón de clases geniales como List<T>, Dictionary<K,V>, HashSet<T>, etc. Estas estructuras hacen un uso intensivo de GetHashCode y Equals. Pero para los tipos de valores, esto requiere boxeo. IEquatable<T> permite a una estructura implementar un método fuertemente tipado Equals por lo que no se requiere boxeo. Por lo tanto, un rendimiento mucho mejor cuando se usan tipos de valores con colecciones genéricas.

Los tipos de referencia no se benefician tanto, pero la implementación IEquatable<T> le permite evitar un reparto de System.Object que puede marcar la diferencia si se lo llama con frecuencia.

Sin embargo, como se indicó en Jared Parson's blog, debe implementar las anulaciones de objeto.

+0

¿Hay alguna conversión entre los tipos de referencia? Siempre pensé que los moldes eran solo "afirmaciones" que usted hace al compilador cuando asigna algunos moldes no obvios de un tipo de objeto a otro. Esto es, después de compilar, que el código ni siquiera sabría que había un molde allí. –

+5

Eso es cierto en C++, pero no en los lenguajes .NET que hacen cumplir la seguridad del tipo. Hay un lanzamiento en tiempo de ejecución y si el lanzamiento no tiene éxito, se lanza una excepción. Entonces, hay una pequeña penalización de tiempo de ejecución para pagar el casting. El compilador puede optimizar las transmisiones fuera de línea. Por ejemplo, el objeto o = (objeto) "cadena"; Pero downcasting - cadena s = (cadena) o; - debe suceder en tiempo de ejecución. – Josh

+1

Ya veo. Por casualidad, ¿tiene algún lugar donde pueda obtener ese tipo de información "más profunda" sobre .NET? ¡Gracias! –

34

De acuerdo con los MSDN:

Si implementa IEquatable(T), que también debe sustituir la clase de base de implementaciones de Object.Equals(Object) y GetHashCode por lo que su comportamiento es consistente con la del método IEquatable(T).Equals . Si sobrescribe Object.Equals(Object), su implementación reemplazada también se llama en las llamadas al método estático Equals(System.Object, System.Object) en su clase. Esto garantiza que todas las invocaciones de el método Equals devuelvan resultados consistentes .

Parece que no hay una diferencia funcional real entre los dos, excepto que cualquiera podría llamarse dependiendo de cómo se usa la clase. Desde el punto de vista del rendimiento, es mejor utilizar la versión genérica porque no hay una penalización de box/unboxing asociada.

Desde un punto de vista lógico, también es mejor implementar la interfaz. Anular el objeto realmente no le dice a nadie que su clase es realmente equitativa. La anulación puede ser simplemente una clase de "no hacer nada" o una implementación superficial. El uso de la interfaz dice explícitamente: "¡Oye, esto es válido para la verificación de igualdad!" Es solo un mejor diseño.

+0

Sí, ambos son buenos puntos. –

+4

Las estructuras definitivamente deberían implementar iEquatable (de theirOwnType) si van a usarse como claves en un diccionario o colección similar; ofrecerá un gran aumento de rendimiento. Las clases no heredables recibirán un ligero aumento de rendimiento implementando IEquatable (de theirOwnType). Las clases heredables deberían // no // implementar IEquatable. – supercat

19

Extendiendo lo que Josh dijo con un ejemplo práctico.+1 a Josh - Estaba a punto de escribir lo mismo en mi respuesta. Iguales

public abstract class EntityBase : IEquatable<EntityBase> 
{ 
    public EntityBase() { } 

    #region IEquatable<EntityBase> Members 

    public bool Equals(EntityBase other) 
    { 
     //Generic implementation of equality using reflection on derived class instance. 
     return true; 
    } 

    public override bool Equals(object obj) 
    { 
     return this.Equals(obj as EntityBase); 
    } 

    #endregion 
} 

public class Author : EntityBase 
{ 
    public Author() { } 
} 

public class Book : EntityBase 
{ 
    public Book() { } 
} 

De esta manera, he re-utilizables() método que funciona fuera de la caja para todas mis clases derivadas.

+0

Solo una pregunta más. ¿Cuál es la ventaja de usar "obj como EntityBase" en lugar de (EntityBase) obj? ¿Solo una cuestión de estilo o hay alguna ventaja? –

+15

en caso de "obj como EntityBase" - si obj no es de tipo EntityBase, pasará "nulo" y continuará sin ningún error o excepción, pero en el caso de "(EntityBase) obj", intentará forzar obj a EntityBase y si el obj no es de tipo EntityBase, lanzará InvalidCastException. Y sí, "como" solo se puede aplicar a tipos de referencia. –

+0

Ah, ya veo. Estupendo. ¡Gracias! –

Cuestiones relacionadas