2010-05-27 38 views
28

Necesito saber la mejor manera de comparar dos objetos y averiguar si no hay igual. Estoy anulando tanto GethashCode como Equals. Así que una clase básica se parece a:C# Cómo encontrar si dos objetos son iguales

public class Test 
{ 
    public int Value { get; set; } 
    public string String1 { get; set; } 
    public string String2 { get; set; } 

    public override int GetHashCode() 
    { 
     return Value^String1.GetHashCode()^String2.GetHashCode(); 
    } 

    public override bool Equals(object obj) 
    { 
     return GetHashCode() == obj.GetHashCode(); 
    } 
} 

lo tanto, para los propósitos de prueba que crea dos objetos:

Test t = new Test() 
{ 
    Value = 1, 
    String1 ="One", 
    String2 = "One" 
}; 

Test t2 = new Test() 
{ 
    Value = 1, 
    String1 = "Two", 
    String2 = "Two" 
}; 

bool areEqual = t.Equals(t2); 

En las pruebas de este areEqual devuelve verdadero caso de que ambos objetos son diferentes. Me doy cuenta de que esto se debe a que String1 y String2 tienen el mismo valor en cada objeto y, por lo tanto, se anulan mutuamente cuando se procesan hash.

¿Hay alguna manera mejor de eliminar el objeto hash que el método que tengo que resolverá mi problema?

Respuesta

37

Su método de igualdad actual está roto: hay más valores que posibles códigos hash. Es completamente razonable (y esperado) que ocasionalmente tengas valores que son desiguales pero dan el mismo hash. Es igual debe comprobar los valores reales:

public override bool Equals(object obj) 
{ 
    Test test = obj as Test; 
    if (obj == null) 
    { 
     return false; 
    } 
    return Value == test.Value && 
     String1 == test.String1 && 
     String2 == test.String2; 
} 

algunas cosas a tener en cuenta:

  • Su manera de generar el código hash dará el mismo valor para cualquier Value fijo si String1 y String2 son la mismo; también explotará si String1 o String2 es nulo. Este es un aspecto desafortunado de usar XOR para hash. Yo prefiero algo como esto:

    // Put this extension method in a utility class somewhere 
    public static int SafeGetHashCode<T>(this T value) where T : class 
    { 
        return value == null ? 0 : value.GetHashCode(); 
    } 
    
    // and this in your actual class 
    public override int GetHashCode() 
    { 
        int hash = 19; 
        hash = hash * 31 + Value; 
        hash = hash * 31 + String1.SafeGetHashCode(); 
        hash = hash * 31 + String2.SafeGetHashCode(); 
        return hash; 
    } 
    
  • En términos generales, la igualdad se vuelve difícil cuando la herencia se involucra. Es posible que desee considerar el sellado de su clase.

  • También es posible que desee implementar IEquatable<Test>

+2

Re 'SafeGetHashCode' - sólo para mencionar que' EqualityComparer .GetHashCode' va a hacer algo muy similar - el método de extensión es bastante ordenado, sin embargo; p –

+0

Su respuesta es muy buena, mientras que la primera palabra es incorrecta: "No" es la respuesta incorrecta a su pregunta: "¿Hay una manera mejor ..." Además, la clase de prueba de Simons no tiene una clase base ni implementa una interfaz. por lo que la palabra clave de anulación está realmente equivocada – OlimilOops

+1

@Oops: Arreglará el "No" ... pero la clase de prueba * does * tiene una clase base, la clase base implícita de System.Object. Estos métodos * están * anulando las implementaciones de objetos. –

16

Su Equals es incorrecto - que debe definir lo que significa que dos cosas son iguales - y tener el mismo hash-código no no igualdad de medias (sin embargo, un código hash diferente does significa no igualdad). Si "igualdad" significa "ambas cadenas son iguales en pares", entonces prueba eso.

Re a better hash; xor es notorio por esto, ya que es trivial obtener 0 por xor un valor consigo mismo. Un mejor enfoque puede ser algo como:

int i = 0x65407627; 
i = (i * -1521134295) + Value.GetHashCode(); 
i = (i * -1521134295) + (String1 == null ? 0 : String1.GetHashCode()); 
i = (i * -1521134295) + (String2 == null ? 0 : String2.GetHashCode()); 
return i; 
+0

(actualizado, tomando prestado el germen y el multiplicador que el compilador C# usa para tipos anónimos) –

+0

¿Por qué eligió esos valores para generar el código hash? –

+0

@Carlos: Vea el comentario anterior de Marc :) –

2

Para cualquier par de objetos, igualdad de objetos implica la igualdad de hash código, sin embargo , la igualdad código hash no implica igualdad de objetos. De Object.GetHashCode en MSDN:

Una función hash debe tener las siguientes propiedades:

Si dos objetos resultan ser iguales, el método GetHashCode para cada objeto debe devolver el mismo valor.Sin embargo, si dos objetos no se comparan como iguales, los métodos GetHashCode para el objeto dos no tienen que devolver valores diferentes.

En otras palabras, su Equals está mal escrito. Debería ser algo como:

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

    return (Value == other.Value) 
     && (String1 == other.String1) 
     && (String2 == other.String2); 
} 

GetHashCode es bueno para las colecciones (como Dictionary<K, V>) para determinar rápidamente la igualdad aproximada. Equals es para comparar si dos objetos son realmente iguales.

3

Además de todas las grandes respuestas que recomendaría que lea este article por Alois Kraus

+0

Sí ¿Me gusta el 'operador ==' anular '? Creo que funciona bien para comparar si los valores reales de mis 2 objetos, para ver si tienen el mismo valor de diferentes fuentes, sin tener que llamar a métodos o extensiones. Creo que esta es una gran respuesta, pero no estoy seguro de qué principio de programación caerá. Puede confundir a algunos desarrolladores? – ppumkin

0

no sería una función es igual a probar siempre sólo contra el mismo tipo, ¿no debería ser:

//override 
    public bool Equals(Test other)//(object obj) 
    { 
     //return GetHashCode() == obj.GetHashCode(); 
     return (Value == other.Value) && 
       (String1 == other.String1) && 
       (String2 == other.String2); 
    } 

respecto Vaya

+0

Usted * debe * anular el método de Object. También * podría * implementar 'IEquatable ', que es cuando debe especificar esta firma. –

+0

hmm no sé, ¿es un problema de la versión beta de VS2010? Si utilizo la anulación aquí, VS dice: 'Test.Equals (Test)': no ​​se encontró ningún método adecuado para anular – OlimilOops

2

bien !!!!! (MSDN)

+0

Formato realmente malvado para ser honesto ... – cyberzed

+0

Por lo general, no es útil copiar varias páginas de MSDN; es mejor vincular y resaltar cualquier fragmento de particular importancia. Pero no toda la página. –

4

sencilla

Object.Equals(obj1, obj2); 
+1

Todo esto depende de si obj1/obj2 son tipos de referencia o no. Consulte http://msdn.microsoft.com/en-us/library/bsc2ak47.aspx –

+0

thx u r right./// –

+0

Esta es en realidad la mejor respuesta, es la más breve, clara y 100% correcta. –

1

Se puede serializar los dos objetos a JSON, a continuación, comparar las dos cadenas para ver si son los mismos.

Por ejemplo

JavaSriptSerializer serialiser = new JavaScriptSerializer(); 

string t1String = serialiser.Serialize(t); 
string t2String = serialiser.Serialize(t2); 

if(t1String == t2String) 
    return true; //they are equal 
else 
    return false; 
+2

no siempre funciona. JSON tiene su propia limitación. Dependiendo de JSON para comparar el objeto general no es una buena idea. – liang

+0

Intenté esto y no funciona. Produce falsos negativos debido a la diferente ordenación de propiedades en el JSON. – NickG

Cuestiones relacionadas