2010-12-12 25 views
10

¿Cuál es su enfoque en la escritura de verificaciones de igualdad para el structs y el classes que crea?C# comprobación de igualdad

1) ¿La igualdad "completa" la comprobación requieren que gran parte de código repetitivo (como override Equals, override GetHashCode, Equals genérica, operator==, operator!=)?

2) ¿Especifica explícitamente que sus clases modelen la interfaz IEquatable<T>?

3) hacer yo entendido bien, que no hay manera real para aplicar automáticamente Equals anulaciones, cuando invocar algo así como a == b y siempre deben ejecutar tanto las Equals y operator== miembros?

Respuesta

20

tienes razón, esto es una gran cantidad de código de la caldera de la placa, y hay que poner en práctica todo por separado.

lo haría recomendar:

  • Si usted va a poner en práctica la igualdad de valor alguno, anular y GetHashCodeEquals(object) - la creación de sobrecargas para == e implementar IEquatable<T> sin hacer que podría resultar en un comportamiento muy inesperado
  • yo siempre aplicar IEquatable<T> si estás anulando Equals(object) y GetHashCode
  • sólo sobrecargar el operador == más raramente
  • la implementación de la igualdad de clases sin sellar correctamente es difícil, y todavía puede producir resultados sorprendentes/indeseables. Si necesita igualdad para los tipos en una jerarquía, implemente IEqualityComparer<T> expresando la comparación que le interesa.
  • La igualdad para los tipos mutables generalmente es una mala idea, ya que dos objetos pueden ser iguales y luego desiguales más adelante ...si un objeto está mutado (de una manera que afecta la igualdad) después de haber sido utilizado como clave en una tabla hash, no podrá encontrarlo nuevamente.
  • Parte de la placa de la caldera es ligeramente diferente para las estructuras ... pero como Marc, rara vez escribo mis propias estructuras.

Aquí está un ejemplo de implementación:

using System; 

public sealed class Foo : IEquatable<Foo> 
{ 
    private readonly string name; 
    public string Name { get { return name; } } 

    private readonly int value; 
    public int Value { get { return value; } } 

    public Foo(string name, int value) 
    { 
     this.name = name; 
     this.value = value; 
    } 

    public override bool Equals(object other) 
    { 
     return Equals(other as Foo); 
    } 

    public override int GetHashCode() 
    { 
     int hash = 17; 
     hash = hash * 31 + (name == null ? 0 : name.GetHashCode()); 
     hash = hash * 31 + value; 
     return hash; 
    } 

    public bool Equals(Foo other) 
    { 
     if ((object) other == null) 
     { 
      return false; 
     } 
     return name == other.name && value == other.value; 
    } 

    public static bool operator ==(Foo left, Foo right) 
    { 
     return object.Equals(left, right); 
    } 

    public static bool operator !=(Foo left, Foo right) 
    { 
     return !(left == right); 
    } 
} 

Y sí, eso es una diablos de un montón de repetitivo, muy poco de lo que cambia entre las implementaciones :(

La implementación de == es ligeramente menos eficiente de lo que podría ser, ya que llamará a Equals(object) que necesita hacer la verificación de tipo dinámico ... pero la alternativa es aún más placa de caldera, como esta:

public static bool operator ==(Foo left, Foo right) 
{ 
    if ((object) left == (object) right) 
    { 
     return true; 
    } 

    // "right" being null is covered in left.Equals(right) 
    if ((object) left == null) 
    { 
     return false; 
    } 
    return left.Equals(right); 
} 
+1

^^ Su cada puesto es un capítulo de aprendizaje C# .. :) – Dienekes

+0

2 sugerencias menores para el segundo bloque de código: 1) ¿No deberías mover '(objeto) izquierda == (objeto) derecho' de' == 'a' Iguales' genéricos? Entonces, ¿eso da la velocidad (por supuesto depende, pero suponiendo el peor de los casos) la comprobación de la igualdad de referencia incluso para el método genérico "Igual"? 2) no necesita la segunda comprobación nula '(objeto) derecha == nulo' en' == 'ya que básicamente hace eso en' Iguales' genéricos. Ver mi publicación .. – nawfal

+0

@nawfal: No creo que tenga mucho sentido hacerlo en el caso genérico 'Equals'; de todos modos será rápido en los casos en que * es * verdadero, y para los casos en que * no es * verdadero, está agregando un cheque adicional sin beneficio. En cuanto a la parte nula, eso requeriría la verificación dinámica de tipo nuevamente. Sí, podría argumentar a favor de ambos, pero estoy satisfecho con lo que escribí hace dos años ... –

1

Solo necesita implementar operator == para a == b.
Como me gustan mis datos en los diccionarios a veces invalido GetHashCode.
Siguiente Implemento Equals (como un estándar no mencionado ... esto es porque no hay restricciones para la igualdad cuando se usan genéricos) y especifique la implementación de IEquatable. Como voy a hacer esto, también podría apuntar mis implementaciones == y! = A Equals. :)

6

Raramente hago algo especial para las clases; para la mayoría de los objetos regulares la igualdad referencial funciona bien.

Aún más raramente escribo un struct; pero dado que las estructuras representan los valores , generalmente es apropiado proporcionar igualdad, etc. Esto generalmente implicaría todo; Es igual, ==,! = Y IEquatable<T> (ya que esto evita el boxeo en los escenarios usando EqualityComparer<T>.Default.

El repetitivo no suele ser demasiado problemático, pero IIRC herramientas como ReSharper puede ayudar aquí.

Sí, es aconsejable para mantener iguales y == en sincronía, y esto necesita ser hecho explícitamente.

Cuestiones relacionadas