2009-03-04 9 views
6

Si anulo Equals y GetHashCode, ¿cómo decido qué campos comparar? ¿Y qué pasará si tengo dos objetos con dos campos cada uno, pero Equals solo comprueba un campo?Ignorar Igual() pero no verificar todos los campos, ¿qué pasará?

En otras palabras, vamos a decir que tengo esta clase:

class EqualsTestClass 
{ 
    public string MyDescription { get; set; } 
    public int MyId { get; set; } 

    public override bool Equals(object obj) 
    { 
     EqualsTestClass eq = obj as EqualsTestClass; 
     if(eq == null) { 
      return false; 
     } else { 
      return MyId.Equals(eq.MyId); 
     } 
    } 

    public override int GetHashCode() 
    { 
     int hashcode = 23; 
     return (hashcode * 17) + MyId.GetHashCode(); 
    } 
} 

que considerar dos objetos iguales si tienen el mismo MyID. Entonces, si el Id es igual pero la descripción es diferente, aún se consideran iguales.

Me pregunto cuáles son los peligros de este enfoque. Por supuesto, se comporta como se esperaba una construcción como esta:

 List<EqualsTestClass> test = new List<EqualsTestClass>(); 

     EqualsTestClass eq1 = new EqualsTestClass(); 
     eq1.MyId = 1; 
     eq1.MyDescription = "Des1"; 

     EqualsTestClass eq2 = new EqualsTestClass(); 
     eq2.MyId = 1; 
     eq2.MyDescription = "Des2"; 

     test.Add(eq1); 
     if (!test.Contains(eq2)) 
     { 
      // Will not be executed, as test.Contains is true 
      test.Add(eq2); 
     } 

Como EQ2 es de valor igual a eq1, no va a ser añadido. Pero ese es el código que yo controlo, pero me pregunto si hay un código en el marco que pueda causar problemas inesperados.

Entonces, ¿debería siempre agregar todos los campos públicos en mi comparación de Equals(), o cuáles son las pautas para evitar una desagradable sorpresa debido a un mal Framework-Mojo que fue completamente inesperado?

Respuesta

11

La razón para anular Equals() es que usted defina lo que significa que dos instancias sean iguales. En algunos casos, eso significa que todos los campos deben ser iguales, pero no tiene que ser así. Tú decides. Para obtener más información, vea documentation y post.

+0

de acuerdo, al igual que el PO, he alterada temporalmente equals() simplemente comprobar una identificación primaria, y eso es todo lo que llaman los programadores. –

1

No creo que deba preocuparse por el Framework en este caso. Si usted, como Diseñador de clases, considera que dos instancias de esa clase son iguales si comparten el mismo MyId, entonces, solo necesita probar MyId en sus métodos sobrescritos Igual() y GetHashCode().

1

Solo necesita marcar los campos que se requieren para que coincidan, si todo lo que necesita para coincidir es la ID, entonces vaya con eso.

1

Pregunta: Si anulo Equals y GetHashCode, ¿cómo decido qué campos comparo?

Depende de lo que está tratando de lograr. Si está tratando de ver si los objetos son exactamente iguales, debería compararlos todos. Si tiene alguna "clave" y solo quiere saber si son el mismo "objeto", incluso si otros datos son diferentes, simplemente verifique los valores de "clave".

¿Y qué pasará si tengo dos objetos con dos campos cada uno, pero igual solo comprueba un campo?

Luego tendrá un método de igualdad que solo verifica si la 'clave' es la misma, y ​​podría tener múltiples objetos 'iguales' que tienen varianzas internas.

1

Otros han dicho que esto es perfectamente válido y esperado, y así es exactamente como debería funcionar Equals. Entonces no hay problema con eso como clase.

Sería un poco cauteloso de esto como una API. Perdóname si no es lo que se pretendía: en ese caso, esto es solo una nota de precaución para los demás.

El problema potencial es que los usuarios de la API esperarán naturalmente que los objetos iguales "sean iguales". Esto no es parte del contrato de igualdad, pero es parte del significado de sentido común de la palabra. La clase se parece un poco a una tupla binaria, pero no es una, por lo que debería ser por razones sensatas.

Un ejemplo de una razón tan sensata es que un campo es un "detalle de implementación visible", como el factor de carga máximo en un contenedor basado en hashtable. Un ejemplo de una razón arriesgada (aunque tentadora) es "porque agregué la descripción después y no quería cambiar el método Equals en caso de que se rompiera algo".

Por lo tanto, es completamente válido hacer algo contraintuitivo, especialmente si documenta claramente que el comportamiento puede ser sorprendente. Esos métodos deben ser compatibles, porque prohibirlos sería una locura en los casos en que un campo es obviamente irrelevante. Pero debe quedar claro por qué tiene sentido crear dos pares de ID descriptivos con la misma ID y diferentes descripciones, pero no tiene sentido agregarlos a un contenedor (como HashSet) que usa Equals/HashCode para evitar duplicados. entradas.

0

En su ejemplo, se comprueba si una lista (de EqualsTestClass) contiene un objeto del mismo tipo con los mismos valores de propiedad. Otra forma de lograr esta tarea sin sobreescribir equals (y la comprensión tradicional de iguales) es usar un comparador personalizado. Se vería algo como esto (en VB):

Public Class EqualsTestComparer 
Implements IEqualityComparer(Of EqualsTestClass) 
Public Function Equals1(ByVal x As EqualsTestClass, ByVal y As EqualsTestClass) As Boolean Implements System.Collections.Generic.IEqualityComparer(Of EqualsTestClass).Equals 
    If x.MyId = y.MyId and x.MyDescription = y.MyDescription Then 
     Return True 
    Else 
     Return False 
    End If 
End Function 

Public Function GetHashCode1(ByVal obj As EqualsTestClass) As Integer Implements System.Collections.Generic.IEqualityComparer(Of EqualsTestClass).GetHashCode 
    Return obj.ToString.ToLower.GetHashCode 
End Function  
End Class 

Luego, en su rutina sólo tiene que utilizar el comparador de encargo:

If Not test.Contains(eq2, New EqualsTestComparer) Then 
    //Do Stuff 
End if 
Cuestiones relacionadas