2010-12-14 24 views
8

Tengo una clase que contiene algunos miembros de cadena, algunos miembros dobles y algunos objetos de matriz.Comparación de objetos

Creo dos objetos de esta clase, ¿hay alguna manera más simple y eficiente de comparar estos objetos y decir su igual? ¿Alguna sugerencia?

Sé cómo escribir una función de comparación, pero llevará mucho tiempo.

+2

¿Cuáles son sus condiciones para que los objetos sean iguales? – BoltClock

+0

Básicamente, todos los valores en estos miembros de cadena, miembros dobles y las matrices deberían ser los mismos. – siva

+1

posible duplicado de http://stackoverflow.com/questions/506096/comparing-object-properties-in-c – RameshVel

Respuesta

11

La única forma en que puede hacer esto es anulando bool Object.Equals(object other) para devolver verdadero cuando sus condiciones de igualdad se cumplen, y devuelva falso de lo contrario. También debe anular int Object.GetHashCode() para devolver un int calculado a partir de todos de los datos que considere al anular Equals().

Como nota aparte, tenga en cuenta que el contrato para GetHashCode() especifica que el valor de retorno debe ser igual para dos objetos cuando Equals() devolvería verdadero al compararlos. Esto significa que return 0; es una implementación válida de GetHashCode() pero causará ineficiencias cuando los objetos de su clase se usan como claves de diccionario, o se almacenan en un HashSet<T>.

La forma implemento la igualdad es la siguiente:

public class Foo : IEquatable<Foo> 
{ 
    public bool Equals(Foo other) 
    { 
     if (other == null) 
      return false; 

     if (other == this) 
      return true; // Same object reference. 

     // Compare this to other and return true/false as appropriate. 
    } 

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

    public override int GetHashCode() 
    { 
     // Compute and return hash code. 
    } 
} 

Una forma sencilla de implementar GetHashCode() es XOR juntos los códigos hash de todos los datos se tiene en cuenta la igualdad en Equals(). Así que si, por ejemplo, las propiedades que Compárese, por la igualdad son string FirstName; string LastName; int Id;, su aplicación podría ser:

public override int GetHashCode() 
{ 
    return (FirstName != null ? FirstName.GetHashCode() : 0)^
     (LastName != null ? LastName.GetHashCode() : 0)^
     Id; // Primitives of <= 4 bytes are their own hash codes 
} 

Me suelen No anulación de los operadores de igualdad, ya que la mayoría de las veces me preocupa la igualdad solo para propósitos de claves de diccionario o colecciones. Solo consideraría anular los operadores de igualdad si es probable que haga más comparaciones por valor que por referencia, ya que es sintácticamente menos detallado. Sin embargo, debe recordar cambiar todos los lugares donde usa == o != en su objeto (incluso en su implementación de Equals()) para usar Object.ReferenceEquals(), o para enviar ambos operandos al object. Esta desagradable atracción (que puede causar recursión infinita en tu prueba de igualdad si no tienes cuidado) es una de las razones principales por las que rara vez anulo estos operadores.

1

La mejor respuesta es implementar IEquatable para su clase; puede que no sea la respuesta que desea escuchar, pero esa es la mejor manera de implementar la equivalencia de valores en .NET.

Otra opción sería el cálculo de un hash único de todos los miembros de su clase y luego hacer comparaciones de valor en contra de ellos, pero eso es aún más el trabajo de escribir una función de comparación;)

+0

El enfoque hash también es propenso a falsos positivos. – cdhowie

+0

Sí, realmente no lo recomiendo. – Aaronontheweb

9

La forma 'adecuada' a hacerlo en .NET es implementar la interfaz IEquatable para su clase:

public class SomeClass : IEquatable<SomeClass> 
{ 
    public string Name { get; set; } 
    public double Value { get; set; } 
    public int[] NumberList { get; set; } 

    public bool Equals(SomeClass other) 
    { 
     // whatever your custom equality logic is 
     return other.Name == Name && 
      other.Value == Value && 
      other.NumberList == NumberList; 
    } 
} 

Sin embargo, si usted realmente quiere hacer las cosas bien, this isn't all you should do.También debe anular los métodos Equals (objeto, objeto) y GetHashCode (objeto) para que, sin importar cómo su código de llamada compare la igualdad (quizás en un diccionario o tal vez en una colección de tipo suelto), su código y no referencia- tipo de igualdad será el factor determinante:

public class SomeClass : IEquatable<SomeClass> 
{ 
    public string Name { get; set; } 
    public double Value { get; set; } 
    public int[] NumberList { get; set; } 

    /// <summary> 
    /// Explicitly implemented IEquatable method. 
    /// </summary> 
    public bool IEquatable<SomeClass>.Equals(SomeClass other) 
    { 
     return other.Name == Name && 
      other.Value == Value && 
      other.NumberList == NumberList; 
    } 

    public override bool Equals(object obj) 
    { 
     var other = obj as SomeClass; 
     if (other == null) 
      return false; 
     return ((IEquatable<SomeClass>)(this)).Equals(other); 
    } 

    public override int GetHashCode() 
    { 
     // Determine some consistent way of generating a hash code, such as... 
     return Name.GetHashCode()^Value.GetHashCode()^NumberList.GetHashCode(); 
    } 
} 
1

Dado que estos son objetos, supongo que tendrá que anular el método Equals para los objetos. De lo contrario, el método Equals le dará la aprobación solo si ambos objetos se refieren al mismo objeto.

Sé que esta no es la respuesta que desea. Pero dado que hay pocas propiedades en su clase, puede anular fácilmente el método.

5

Acabo de pasar todo el día escribiendo un método de extensión describiendo las propiedades de un objeto con varias partes complejas de la lógica para tratar diferentes tipos de propiedades y lo puse cerca del bien, entonces a las 16:55 me di cuenta que si serializar los dos objetos, simplemente necesita comparar las dos cadenas ... duh

Así que aquí es un método simple extensión serializador que incluso funciona con diccionarios

public static class TExtensions 
    { 
     public static string Serialize<T>(this T thisT) 
     { 
      var serializer = new DataContractSerializer(thisT.GetType()); 
      using (var writer = new StringWriter()) 
      using (var stm = new XmlTextWriter(writer)) 
      { 
       serializer.WriteObject(stm, thisT); 
       return writer.ToString(); 
      } 
     } 
} 

Ahora su prueba puede ser tan simple como

Asset.AreEqual(objA.Serialise(), objB.Serialise()) 

No he hecho pruebas exhaustivas todavía, pero parece prometedor y, lo que es más importante, simple. De cualquier manera sigue siendo un método útil para tener en su conjunto de herramientas ¿verdad?

+0

Pero si su objeto contiene propiedades de cadena, ¿las cajas superiores/inferiores no romperán la comparación? Quiero decir, 'UnitedStates' no sería igual a' unitedStates'. –

Cuestiones relacionadas