2010-12-22 28 views
7

Me preguntaba cómo encontraría la diferencia entre dos objetos de la misma clase. Entonces, si tuviera una clase Persona con la única diferencia que es Edad, devolverá el campo/campos que son diferentes.diferencias entre dos objetos en C#

Gracias

+0

¿Quieres preguntar, si hay un comparador objeto genérico que compara dos objetos del mismo tipo y da los nombres de las propiedades que difieren? –

Respuesta

9

esto no es algo que C# (o .NET en realidad) apoya directamente, sin embargo se podrían poner en práctica algo a mano para tipos específicos, o escribir código que utiliza la reflexión para diff objetos arbitrarios.

Si elige el más tarde, tendrá que decidir qué tan profundo quiere entrar en el gráfico de objetos para identificar si dos instancias son idénticas o no y cómo comparará ciertos tipos primitivos para igualdad (dobles, por ejemplo) .

Escribir un algoritmo de diferenciación basado en la reflexión es más difícil de lo que parece al principio - personalmente, implementaría esta funcionalidad directamente para los tipos (o dentro de una clase de ayuda) donde lo necesite.

+1

¿por qué es necesario decidir qué tan profundo ir? ¿no es posible escribir un algoritmo recursivo que lo consiga? – fearofawhackplanet

+1

@fearofawhackplanet: esta es una pregunta sobre requisitos. No siempre es significativo intentar comparar un gráfico de objetos con una profundidad infinita. También es más complicado, ya que requiere la detección de ciclos en gráficos arbitrariamente complejos, lo que es posible pero puede ser complicado. – LBushkin

+0

No tengo idea de lo que significa este último bit, pero tomaré su palabra :) – fearofawhackplanet

1

Deberá recorrer recursivamente todas las propiedades y campos públicos y privados en todo el gráfico de objetos. Utilice un HashSet para realizar un seguimiento de los objetos que ya ha comprobado para que no devuelva resultados duplicados ni entre en un desbordamiento de pila.

Si el tipo de propiedad es IComparable, puede convertir los valores de esa propiedad a IComparable y usar IComparable.CompareTo. De lo contrario, tendrá que llamar recursivamente a su método diferencial en los subobjetos.

4

Realmente depende de qué tan profundo quieras comparar las entidades, pero la idea con reflexión es la mejor aquí. El código sería algo así:

public class Pair 
{ 
    public object Value1 
    { 
     get; 
     set; 
    } 

    public object Value2 
    { 
     get; 
     set; 
    } 
} 

//somewhere in another class 

public Dictionary<string, Pair> Compare<T>(T object1, T object2) 
{ 
    var props = typeof(T).GetProperties().Where(pi => pi.CanRead); //this will return only public readable properties. Modify if you need something different 
    Dictionary<string, Pair> result = new Dictionary<string, Pair>(); 
    foreach (var prop in props) 
    { 
     var val1 = prop.GetValue(object1, null); //indexing properties are ignored here 
     var val2 = prop.GetValue(object2, null); 
     if (val1 != val2) //maybe some more sophisticated compare algorithm here, using IComparable, nested objects analysis etc. 
     { 
      result[prop.Name] = new Pair { Value1 = val1, Value2 = val2 }; 
     } 
    } 
    return result; 
} 

Si necesita procesar profundamente objetos anidados, pues, como se dijo antes, se necesita algún tabla hash que recordar objetos que ya están procesados ​​y evitar que sean procesados de nuevo. ¡Espero que esto ayude!

2

¿Qué pasa algo como esto

Esto se obtiene una lista de los nombres de propiedades que son diferentes entre los dos objetos. No creo que esto es todo el camino a la solución que está buscando, pero yo creo que es un buen comienzo

Foo foo1 = new Foo { Prop1 = "One", Prop2 = "Two"}; 
Foo foo2 = new Foo { Prop1 = "One", Prop2 = "Three" }; 

Type fooType = typeof (Foo); 

PropertyInfo[] properties = fooType.GetProperties(); 

var diffs = from property in properties 
    let first = foo1 
    let second = foo2 
    where property.GetValue(first, null) != property.GetValue(second, null) 
    select property; 

En mi ejemplo devolvería "Prop2" ya que es la propiedad cuyos valores difieren Transcurrirá los objetos.

EDIT: Por supuesto, se supone que cualquier tipo complejo en su objeto implementa comparaciones de igualdad que hacen lo que espera. Si no es lo que se necesita para bucear por el gráfico de objetos y no anidado compara como han sugerido otros

8

Aquí hay un código sencillo que utilizo para sólo una cosa así durante la depuración:

//This structure represents the comparison of one member of an object to the corresponding member of another object. 
    public struct MemberComparison 
    { 
     public readonly MemberInfo Member; //Which member this Comparison compares 
     public readonly object Value1, Value2;//The values of each object's respective member 
     public MemberComparison(MemberInfo member, object value1, object value2) 
     { 
      Member = member; 
      Value1 = value1; 
      Value2 = value2; 
     } 

     public override string ToString() 
     { 
      return Member.Name + ": " + Value1.ToString() + (Value1.Equals(Value2) ? " == " : " != ") + Value2.ToString(); 
     } 
    } 

    //This method can be used to get a list of MemberComparison values that represent the fields and/or properties that differ between the two objects. 
    public List<MemberComparison> ReflectiveCompare<T>(T x, T y) 
    { 
     List<MemberComparison> list = new List<MemberComparison>();//The list to be returned 

     foreach (MemberInfo m in typeof(T).GetMembers(BindingFlags.NonPublic | BindingFlags.Instance)) 
      //Only look at fields and properties. 
      //This could be changed to include methods, but you'd have to get values to pass to the methods you want to compare 
      if (m.MemberType == MemberTypes.Field) 
      { 
       FieldInfo field = (FieldInfo)m; 
       var xValue = field.GetValue(x); 
       var yValue = field.GetValue(y); 
       if (!object.Equals(xValue, yValue))//Add a new comparison to the list if the value of the member defined on 'x' isn't equal to the value of the member defined on 'y'. 
        list.Add(new MemberComparison(field, yValue, xValue)); 
      } 
      else if (m.MemberType == MemberTypes.Property) 
      { 
       var prop = (PropertyInfo)m; 
       if (prop.CanRead && prop.GetGetMethod().GetParameters().Length == 0) 
       { 
        var xValue = prop.GetValue(x, null); 
        var yValue = prop.GetValue(y, null); 
        if (!object.Equals(xValue, yValue)) 
         list.Add(new MemberComparison(prop, xValue, yValue)); 
       } 
       else//Ignore properties that aren't readable or are indexers 
        continue; 
      } 
     return list; 
    } 

Para usarlo, su código podría ser algo como esto:

public static void Main() 
{ 
    MyObject object1 = new MyObject(); 
    MyObject object2 = new MyObject(); 
    // ...Code that changes object1 and/or object2... 

    //Here's your answer: a list of what's different between the 2 objects, and each of their different values. 
    //No type parameters are needed here- typeof(MyObject) is implied by the coincident types of both parameters. 
    List<MemberComparison> changes = ReflectiveCompare(object1, object2); 
} 
+2

Me gusta la solución, pero la comparación debe ser:! Object.Equals (xValue, yValue) para manejar cuando yValue es nulo. – DShook

3

utilicé la respuesta de Michael Hoffmann, sin embargo me pareció que faltaba apoyo si una de las propiedades es nulo, y si uno tira un error (normalmente se encuentra al comparar objetos "tipo"), o si uno es una colección.

mientras que todavía hay trabajo por hacer, estoy publicando aquí el código modificado de base:

public struct MemberComparison 
    { 
     public readonly System.Reflection.MemberInfo Member; //Which member this Comparison compares 
     public readonly object Value1, Value2;//The values of each object's respective member 
     public readonly Exception Value1Exception, Value2Exception; 
     public MemberComparison(System.Reflection.MemberInfo member, object value1, object value2, Exception value1Exception = null, Exception value2Exception = null) 
     { 
      Member = member; 
      Value1 = value1; 
      Value2 = value2; 
      Value1Exception = value1Exception; 
      Value2Exception = value2Exception; 
     } 

     public override string ToString() 
     { 
      if (Value1Exception != null && Value2Exception != null) 
      { 
       if (Value1Exception.GetType().Equals(Value2Exception.GetType())) 
       { 
        return Member.Name + ": Exception in both, same exception type of type "+Value1Exception.GetType().Name+", message in first exception: " +Value1Exception.Message+", message in second exception: "+Value2Exception.Message+", differences in type value: " + string.Join("\n", ReflectiveCompare(Value1Exception, Value2Exception).ToArray()); 
       } 
       else if (!Value1Exception.GetType().Equals(Value2Exception.GetType())) 
       { 
        return Member.Name + ": Exception in both, different exception type: " + Value1Exception.GetType().Name + " : " + Value2Exception.GetType().Name+", message in first exception: " +Value1Exception.Message+", message in second exception: "+Value2Exception.Message; 
       }      
      } 
      else if (Value1Exception != null && Value2Exception == null) 
      {      
        return Member.Name + ": "+ Value2.ToString()+" Exception in first of type " + Value1Exception.GetType().Name+", message is: "+Value1Exception.Message; 
      } 
      else if (Value1Exception == null && Value2Exception != null) 
      {      
        return Member.Name + ": "+ Value1.ToString()+" Exception in second of type " + Value2Exception.GetType().Name+", message is: "+Value2Exception.Message; 
      }     
      return Member.Name + ": " + Value1.ToString() + (Value1.Equals(Value2) ? " == " : " != ") + Value2.ToString(); 
     } 
    } 


    public static bool isCollection(object obj) 
    { 
     return obj.GetType().GetInterfaces() 
    .Any(iface => (iface.GetType() == typeof(ICollection) || iface.GetType() == typeof(IEnumerable) || iface.GetType() == typeof(IList)) || (iface.IsGenericTypeDefinition && (iface.GetGenericTypeDefinition() == typeof(ICollection<>) || iface.GetGenericTypeDefinition() == typeof(IEnumerable<>) || iface.GetGenericTypeDefinition() == typeof(IList<>)))); 
    } 

//This method can be used to get a list of MemberComparison values that represent the fields and/or properties that differ between the two objects. 
public static List<MemberComparison> ReflectiveCompare<T>(T x, T y) 
{ 
    List<MemberComparison> list = new List<MemberComparison>();//The list to be returned 

    var memb = typeof(T).GetMembers(); 
    foreach (System.Reflection.MemberInfo m in memb) 
     //Only look at fields and properties. 
     //This could be changed to include methods, but you'd have to get values to pass to the methods you want to compare 
     if (m.MemberType == System.Reflection.MemberTypes.Field) 
     { 
      System.Reflection.FieldInfo field = (System.Reflection.FieldInfo)m; 
      Exception excep1 = null; 
      Exception excep2 = null; 
      object xValue = null; 
      object yValue = null; 
      try 
      { 
       xValue = field.GetValue(x); 
      } 
      catch (Exception e) 
      { 
       excep1 = e; 
      } 
      try 
      { 
       yValue = field.GetValue(y); 
      } 
      catch (Exception e) 
      { 
       excep2 = e; 
      } 
      if ((excep1 != null && excep2 == null) || (excep1 == null && excep2 != null)) { list.Add(new MemberComparison(field, yValue, xValue, excep1, excep2)); } 
      else if (excep1 != null && excep2 != null && !excep1.GetType().Equals(excep2.GetType())) { list.Add(new MemberComparison(field, yValue, xValue, excep1, excep2)); } 
      else if (excep1 != null && excep2 != null && excep1.GetType().Equals(excep2.GetType()) && ReflectiveCompare(excep1, excep2).Count > 0) { list.Add(new MemberComparison(field, yValue, xValue, excep1, excep2)); } 
      else if ((xValue == null && yValue == null)) { continue; } 
      else if (xValue == null || yValue == null) list.Add(new MemberComparison(field, yValue, xValue)); 
      else if (!xValue.Equals(yValue) && ((!isCollection(xValue) && !isCollection(yValue)) || (isCollection(xValue) && !isCollection(yValue)) || (!isCollection(xValue) && isCollection(yValue)) || (isCollection(xValue) && isCollection(yValue) && ReflectiveCompare(xValue, yValue).Count > 0)))//Add a new comparison to the list if the value of the member defined on 'x' isn't equal to the value of the member defined on 'y'. 
       list.Add(new MemberComparison(field, yValue, xValue)); 
     } 
     else if (m.MemberType == System.Reflection.MemberTypes.Property) 
     { 
      var prop = (System.Reflection.PropertyInfo)m; 
      if (prop.CanRead && !(prop.GetGetMethod() == null || prop.GetGetMethod().GetParameters() == null) && prop.GetGetMethod().GetParameters().Length == 0) 
      {      
       Exception excep1 = null; 
       Exception excep2 = null; 
       object xValue = null; 
       object yValue = null; 
       try 
       { 
        xValue = prop.GetValue(x, null); 
       } 
       catch (Exception e) 
       { 
        excep1 = e; 
       } 
       try 
       { 
        yValue = prop.GetValue(y, null); 
       } 
       catch (Exception e) 
       { 
        excep2 = e; 
       } 
       if ((excep1 != null && excep2 == null) || (excep1 == null && excep2 != null)) { list.Add(new MemberComparison(prop, yValue, xValue, excep1, excep2)); } 
       else if (excep1 != null && excep2 != null && !excep1.GetType().Equals(excep2.GetType())) { list.Add(new MemberComparison(prop, yValue, xValue, excep1, excep2)); } 
       else if (excep1 != null && excep2 != null && excep1.GetType().Equals(excep2.GetType()) && ReflectiveCompare(excep1, excep2).Count > 0) { list.Add(new MemberComparison(prop, yValue, xValue, excep1, excep2)); } 
       else if ((xValue == null && yValue == null)) { continue; } 
       else if (xValue == null || yValue == null) list.Add(new MemberComparison(prop, yValue, xValue)); 
       else if (!xValue.Equals(yValue) && ((!isCollection(xValue) && !isCollection(yValue)) || (isCollection(xValue) && !isCollection(yValue)) || (!isCollection(xValue) && isCollection(yValue)) || (isCollection(xValue) && isCollection(yValue) && ReflectiveCompare(xValue,yValue).Count > 0)))// || (isCollection(xValue) && isCollection(yValue) && ((IEnumerable<T>)xValue).OrderBy(i => i).SequenceEqual(xValue.OrderBy(i => i)))))) 
        list.Add(new MemberComparison(prop, xValue, yValue)); 
      } 
      else//Ignore properties that aren't readable or are indexers 
       continue; 
     } 
    return list; 
     } 
Cuestiones relacionadas