2012-01-19 35 views
5

Tengo una clase con muchas propiedades. Una copia superficial es suficiente para replicar completamente el objeto.Comparando las propiedades de dos objetos simplemente en C#

Necesito comparar un objeto solo para comprobar si contiene exactamente los mismos valores que otro.

Mis ideas:

La primera y más obvia solución es sólo para crear un enorme bloque método que compara cada propiedad, una después de la otra.

El segundo sería serializar cada objeto y cortar el archivo o hacer algún tipo de suma de comprobación md5 en él. (¿Esto realmente funcionaría)?

El tercero es hacer algún tipo de reflexión sobre el objeto, lo que automatizaría la primera opción, pero crearía un nivel adicional de complejidad.

La velocidad no es realmente un problema.

Interesado en escuchar el pensamiento, o cualquier otro método que me falta para hacer tal cosa.

Edit: Gracias a todos. Mi solución: (Modificado para ahora ser recursivo en artículos genéricos).

public static bool IsSame<T>(T objA, T objB) 
     { 
      var type = typeof(T); 
      var properties = type.GetProperties(); 
      foreach (var pi in properties.Where(a => a.CanRead)) 
      { 
       if (pi.Name == "Item") 
       { 
        var piCount = properties.FirstOrDefault(a => a.Name == "Count"); 
        int count = -1; 
        if (piCount != null && piCount.PropertyType == typeof(System.Int32)) 
         count = (int)piCount.GetValue(objA, null); 
        if (count > 0) 
        { 
         for (int i = 0; i < count; i++) 
         { 
          dynamic child1 = pi.GetValue(objA, new object[] { i }); 
          dynamic child2 = pi.GetValue(objB, new object[] { i }); 
          return IsSame(child1, child2); 
         } 
        } 
       } 
       else 
       { 
        var val1 = type.GetProperty(pi.Name).GetValue(objA, null); 
        var val2 = type.GetProperty(pi.Name).GetValue(objB, null); 
        if (val1 != val2 && (val1 == null || !val1.Equals(val2))) 
         return false; 
       } 
      } 
      return true; 
     } 
+2

iría con la reflexión a menos que la estructura de la clase no sea propensa a cambiar. si * no * cambia, elegiría la primera solución (fácil de leer, fácil de mantener) – Alex

+0

http://stackoverflow.com/questions/506096/comparing-object-properties-in-c-sharp there's solución usando reflexión. – Giedrius

Respuesta

6

La mayoría de los serializadores están diseñados para asegurar que los datos conserva su integridad durante la serialización y deserialización, no para producir un formato serializado consistente. Evitaría usar la serialización para este propósito.

Puede considerar la implementación de IEquatable, para que cada instancia sea capaz de compararse con instancias del mismo tipo. O una clase hacer las comparaciones para usted que implementa IEqualityComparer. Cómo hacen esta comparación puede ser el "gran método" que compara propiedades una después de la otra, o usa la reflexión.

La reflexión puede ser una forma bastante rápida y sencilla de lograr su objetivo pero puede causar problemas (por ejemplo, si alguien agrega una propiedad a su tipo que no debería incluirse para comparar la igualdad), pero obviamente el inverso también es cierto (alguien agrega una propiedad que debe verificarse para determinar la igualdad, pero la comparación de igualdad no se actualiza). El enfoque que utilices generalmente debe decidirse por la comodidad del equipo con cada enfoque en conjunto con el contexto en el que se utilizará la clase.

En su caso, probablemente recomendaría utilizar el enfoque basado en la reflexión ya que desea comprobar el resultado de una operación de clonación superficial, por lo que todas las propiedades deberían ser iguales.

Como comentario adicional, recomendaría usar el método MemberwiseClone para crear el clon, lo que disminuiría la necesidad de tales pruebas rigurosas.

+3

Creo que querrías IEquatable en lugar de IComparable. IComparable es para clasificación, no equivalencia. –

+0

@ForgottenSemicolon Gracias, respuesta actualizada. –

+0

+1 por sugerir buenas interfaces para observar la implementación. – pseudocoder

1

Otra idea es que si las propiedades devuelven tipos de valores simples, podría agruparlos en tipos de valor inmutables propios. Por ejemplo, si tiene un cliente con propiedades string Address1string Address2string Citystring Statestring Zip, podría crear un tipo de valor Dirección que implemente su propio comparador de igualdad y úselo.

No estoy seguro de si esto se aplica a su situación, pero he descubierto que cuando tengo una clase con muchas propiedades, a menudo es posible hacerlo, y esto hace que mis clases sean más simples y fáciles de razonar acerca de.

6

La tercera opción (reflexión) sería la más lenta, pero también sería la más robusta/capaz de prueba.

El código Hash sería muy similar a la primera opción, ya que tendría que llamar a todas sus variables miembro, por lo que 1 y 2 requerirían que actualice sus métodos .Equals(obj) y .GenerateHash() cada vez que modifique las listas de variables miembro .

Aquí hay un código para que pueda empezar:

 foreach (FieldInfo field in this.GetType().GetFields()) 
     { 
      if (o[field.Name] == null) 
      { 
       if (!field.GetValue(this).Equals(o[field.Name])) 
        return false; 
      } 
      else 
      { 
       return false; 
      } 
     } 

     return true; 
1

Si serializar tiene que especificar el orden de serialización en cada campo/propiedad para asegurarse de que todos los datos son serializados int el mismo orden. Pero todo lo que lograría es implementar GetHashCode() en una clase externa.

GetHashCode() tiene algunos ejemplos sobre cómo sobrescribirlo, así que primero hay que mirar.

Si su clase tiene muchos campos, y no desea escribir todo el código a mano. Podría usar el reflejo para autogenerar el código. Si su clase cambia de vez en cuando, entonces podría crear una clase parcial con la implementación GetHashCode() en una plantilla T4.

Cuestiones relacionadas