différences entre deux objects en C #

Je me demandais comment trouver la différence entre deux objects de la même classe. Donc, si j’avais une classe Personne avec la seule différence que c’est l’âge, cela retournera le ou les champs qui sont différents.

Merci

Ce n’est pas quelque chose que C # (ou .NET) supporte directement, cependant, vous pouvez implémenter quelque chose à la main pour des types spécifiques, ou écrire du code qui utilise la reflection pour différencier des objects arbitraires.

Si vous choisissez ce dernier choix, vous devrez décider de la profondeur à laquelle vous voulez aller dans le graphe d’objects pour déterminer si deux instances sont identiques ou non et comment vous comparerez certains types primitifs à l’égalité (doubles, par exemple).

L’écriture d’un algorithme de différenciation basé sur la reflection est plus difficile qu’il n’y paraît au premier abord. Personnellement, je mettrais en œuvre cette fonctionnalité directement pour les types (ou au sein d’une classe d’assistance) où vous en avez besoin.

Voici un code simple que j’utilise pour une telle chose lors du débogage:

//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 ssortingng ToSsortingng() { return Member.Name + ": " + Value1.ToSsortingng() + (Value1.Equals(Value2) ? " == " : " != ") + Value2.ToSsortingng(); } } //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 ReflectiveCompare(T x, T y) { List list = new List();//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; } 

Pour l’utiliser, votre code pourrait ressembler à ceci:

 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 changes = ReflectiveCompare(object1, object2); } 

Cela dépend vraiment de la profondeur à laquelle vous voulez comparer les entités, mais l’idée avec reflection est la meilleure. Le code serait quelque chose comme ça:

 public class Pair { public object Value1 { get; set; } public object Value2 { get; set; } } //somewhere in another class public Dictionary Compare(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 result = new Dictionary(); 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 vous devez traiter en profondeur des objects nesteds, alors, comme il a été dit précédemment, vous aurez besoin d’une table de hachage qui mémorisera les objects déjà traités et les empêchera d’être traités à nouveau. J’espère que cela t’aides!

J’ai utilisé la réponse de Michael Hoffmann, mais j’ai trouvé qu’il manquait de support si l’une des propriétés était nulle et si une erreur était générée (généralement trouvée lors de la comparaison d’objects “Type”), ou s’il s’agissait d’une collection.

alors qu’il rest encore du travail à faire, je poste ici le code de base modifié:

  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 ssortingng ToSsortingng() { 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: " + ssortingng.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.ToSsortingng()+" Exception in first of type " + Value1Exception.GetType().Name+", message is: "+Value1Exception.Message; } else if (Value1Exception == null && Value2Exception != null) { return Member.Name + ": "+ Value1.ToSsortingng()+" Exception in second of type " + Value2Exception.GetType().Name+", message is: "+Value2Exception.Message; } return Member.Name + ": " + Value1.ToSsortingng() + (Value1.Equals(Value2) ? " == " : " != ") + Value2.ToSsortingng(); } } 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 ReflectiveCompare(T x, T y) { List list = new List();//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)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; } 

Qu’en est-il quelque chose comme ça

Cela vous donne une liste des noms de propriété qui sont différents entre les deux objects. Je ne pense pas que ce soit tout le chemin à la solution que vous recherchez mais je pense que c’est un bon début

 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; 

Dans mon exemple, cela renverrait “Prop2” car c’est la propriété dont les valeurs diffèrent entre les objects.

EDIT : Bien entendu, cela suppose que tous les types complexes de votre object implémentent des comparaisons d’égalité qui font ce que vous attendez. Sinon, vous devrez plonger dans le graphe d’objects et faire des comparaisons nestedes comme d’autres l’ont suggéré.

Vous devrez parcourir de manière récursive toutes les propriétés et les champs privés et publics de tout le graphe d’objects. Utilisez un hachage pour garder la trace des objects que vous avez déjà vérifiés afin de ne pas renvoyer les résultats en double ni entrer dans un débordement de stack.

Si le type de propriété est IComparable, vous pouvez convertir les valeurs de cette propriété en IComparable et utiliser IComparable.CompareTo. Sinon, vous devrez appeler récursivement la méthode différentielle sur les sous-objects.