Der beste Weg, um zwei komplexe Objekte zu vergleichen

Ich habe zwei komplexe Objekte wie Object1 und Object2 . Sie haben ungefähr 5 Ebenen von Kinderobjekten.

Ich brauche die schnellste Methode zu sagen, ob sie gleich sind oder nicht.

Wie könnte dies in C # 4.0 getan werden?

   

Implementieren Sie IEquatable (normalerweise in Verbindung mit dem Überschreiben der geerbten Methoden Object.Equals und Object.GetHashCode ) für alle benutzerdefinierten Typen. Bei zusammengesetzten Typen rufen Sie die Equals Methode der enthaltenen Typen innerhalb der enthaltenden Typen auf. Verwenden Sie für enthaltene Auflistungen die Erweiterungsmethode SequenceEqual , die IEquatable.Equals oder Object.Equals für jedes Element intern aufruft. Dieser Ansatz erfordert natürlich, dass Sie die Definitionen Ihrer Typen erweitern, aber die Ergebnisse sind schneller als bei allen generischen Lösungen mit Serialisierung.

Bearbeiten : Hier ist ein künstliches Beispiel mit drei Ebenen der Verschachtelung.

Für Werttypen können Sie normalerweise einfach ihre Equals Methode aufrufen. Selbst wenn die Felder oder Eigenschaften nie explizit zugewiesen wurden, hätten sie immer noch einen Standardwert.

Für Referenztypen sollten Sie zuerst ReferenceEquals aufrufen, das nach Referenzgleichheit sucht – dies würde als Effizienzsteigerung dienen, wenn Sie zufällig auf dasselbe Objekt verweisen. Es würde auch Fälle behandeln, in denen beide Referenzen null sind. Wenn diese Überprüfung fehlschlägt, überprüfen Sie, ob das Feld oder die Eigenschaft Ihrer Instanz nicht null ist (um NullReferenceException zu vermeiden) und rufen Sie ihre Equals Methode auf. Da unsere Member korrekt typisiert sind, wird die Methode IEquatable.Equals direkt aufgerufen, wobei die überschriebene Methode Object.Equals wird (deren Ausführung wegen der Typumwandlung geringfügig langsamer wäre).

Wenn Sie Object.Equals überschreiben, wird auch erwartet, dass Sie Object.GetHashCode überschreiben. Ich habe das nicht im Interesse der Prägnanz getan.

 public class Person : IEquatable { public int Age { get; set; } public string FirstName { get; set; } public Address Address { get; set; } public override bool Equals(object obj) { return this.Equals(obj as Person); } public bool Equals(Person other) { if (other == null) return false; return this.Age.Equals(other.Age) && ( object.ReferenceEquals(this.FirstName, other.FirstName) || this.FirstName != null && this.FirstName.Equals(other.FirstName) ) && ( object.ReferenceEquals(this.Address, other.Address) || this.Address != null && this.Address.Equals(other.Address) ); } } public class Address : IEquatable
{ public int HouseNo { get; set; } public string Street { get; set; } public City City { get; set; } public override bool Equals(object obj) { return this.Equals(obj as Address); } public bool Equals(Address other) { if (other == null) return false; return this.HouseNo.Equals(other.HouseNo) && ( object.ReferenceEquals(this.Street, other.Street) || this.Street != null && this.Street.Equals(other.Street) ) && ( object.ReferenceEquals(this.City, other.City) || this.City != null && this.City.Equals(other.City) ); } } public class City : IEquatable { public string Name { get; set; } public override bool Equals(object obj) { return this.Equals(obj as City); } public bool Equals(City other) { if (other == null) return false; return object.ReferenceEquals(this.Name, other.Name) || this.Name != null && this.Name.Equals(other.Name); } }

Serialisieren Sie beide Objekte und vergleichen Sie die resultierenden Strings

Sie können die Erweiterungsmethode Rekursion verwenden, um dieses Problem zu lösen:

 public static bool DeepCompare(this object obj, object another) { if (ReferenceEquals(obj, another)) return true; if ((obj == null) || (another == null)) return false; //Compare two object's class, return false if they are difference if (obj.GetType() != another.GetType()) return false; var result = true; //Get all properties of obj //And compare each other foreach (var property in obj.GetType().GetProperties()) { var objValue = property.GetValue(obj); var anotherValue = property.GetValue(another); if (!objValue.Equals(anotherValue)) result = false; } return result; } public static bool CompareEx(this object obj, object another) { if (ReferenceEquals(obj, another)) return true; if ((obj == null) || (another == null)) return false; if (obj.GetType() != another.GetType()) return false; //properties: int, double, DateTime, etc, not class if (!obj.GetType().IsClass) return obj.Equals(another); var result = true; foreach (var property in obj.GetType().GetProperties()) { var objValue = property.GetValue(obj); var anotherValue = property.GetValue(another); //Recursion if (!objValue.DeepCompare(anotherValue)) result = false; } return result; } 

oder vergleichen Sie mit Json (wenn Objekt sehr komplex ist) Sie können Newtonsoft.Json verwenden:

 public static bool JsonCompare(this object obj, object another) { if (ReferenceEquals(obj, another)) return true; if ((obj == null) || (another == null)) return false; if (obj.GetType() != another.GetType()) return false; var objJson = JsonConvert.SerializeObject(obj); var anotherJson = JsonConvert.SerializeObject(another); return objJson == anotherJson; } 

Wenn Sie IEquatable nicht implementieren möchten, können Sie Reflection immer zum Vergleichen aller Eigenschaften verwenden. Wenn sie Werttypen sind, vergleichen Sie sie einfach. Wenn sie ein Referenztyp sind, rufen Sie die function rekursiv auf, um ihre “inneren” Eigenschaften zu vergleichen.

Ich denke nicht an performance, sondern an Einfachheit. Es hängt jedoch von der genauen Gestaltung Ihrer Objekte ab. Je nach Objektform kann es kompliziert werden. Es gibt jedoch mehrere Lösungen, die Sie verwenden können, wie diese:

  • Vergleichen Sie .NET-Objekte

Ich weiß nicht, ob du am schnellsten den schnellsten Weg zur Implementierung oder einen Code, der schnell läuft, meinst. Sie sollten nicht optimieren, bevor Sie wissen, ob Sie müssen. Vorzeitige Optimierung ist die Wurzel allen Übels

Serialisieren Sie beide Objekte und vergleichen Sie die resultierenden Strings mit @JoelFan

Um dies zu tun, erstellen Sie eine statische class wie diese und verwenden Sie Extensions, um ALLE Objekte zu erweitern (so dass Sie jede Art von Objekt, Sammlung usw. in die Methode übergeben können).

 using System; using System.IO; using System.Runtime.Serialization.Json; using System.Text; public static class MySerializer { public static string Serialize(this object obj) { var serializer = new DataContractJsonSerializer(obj.GetType()); using (var ms = new MemoryStream()) { serializer.WriteObject(ms, obj); return Encoding.Default.GetString(ms.ToArray()); } } } 

Sobald Sie diese statische class in einer anderen Datei referenzieren, können Sie Folgendes tun:

 Person p = new Person { Firstname = "Jason", LastName = "Argonauts" }; Person p2 = new Person { Firstname = "Jason", LastName = "Argonaut" }; //assuming you have already created a class person! string personString = p.Serialize(); string person2String = p2.Serialize(); 

Jetzt können Sie einfach .Equals verwenden, um sie zu vergleichen. Ich verwende dies, um zu prüfen, ob sich Objekte auch in Sammlungen befinden. Es funktioniert wirklich gut.

Ich nehme an, Sie beziehen sich nicht auf buchstäblich die gleichen Objekte

 Object1 == Object2 

Sie könnten darüber nachdenken, einen Speichervergleich zwischen den beiden zu machen

 memcmp(Object1, Object2, sizeof(Object.GetType()) 

Aber das ist nicht einmal echter Code in c # :). Da alle Ihre Daten wahrscheinlich auf dem Heap erstellt werden, ist der Speicher nicht zusammenhängend und Sie können nicht einfach die Gleichheit zweier Objekte auf agnostische Weise vergleichen. Sie müssen jeden Wert einzeln einzeln vergleichen.

IEquatable Sie, die IEquatable -Schnittstelle zu Ihrer class hinzuzufügen und eine benutzerdefinierte Equals Methode für Ihren Typ zu definieren. Testen Sie dann bei diesem Verfahren jeden Wert manuell. Fügen Sie IEquatable erneut zu den eingeschlossenen Typen hinzu, wenn Sie können, und wiederholen Sie den Vorgang.

 class Foo : IEquatable { public bool Equals(Foo other) { /* check all the values */ return false; } } 

Ich fand diese function unten zum Vergleichen von Objekten.

  static bool Compare(T Object1, T object2) { //Get the type of the object Type type = typeof(T); //return false if any of the object is false if (object.Equals(Object1, default(T)) || object.Equals(object2, default(T))) return false; //Loop through each properties inside class and get values for the property from both the objects and compare foreach (System.Reflection.PropertyInfo property in type.GetProperties()) { if (property.Name != "ExtensionData") { string Object1Value = string.Empty; string Object2Value = string.Empty; if (type.GetProperty(property.Name).GetValue(Object1, null) != null) Object1Value = type.GetProperty(property.Name).GetValue(Object1, null).ToString(); if (type.GetProperty(property.Name).GetValue(object2, null) != null) Object2Value = type.GetProperty(property.Name).GetValue(object2, null).ToString(); if (Object1Value.Trim() != Object2Value.Trim()) { return false; } } } return true; } 

Ich benutze es und es funktioniert gut für mich.

 public class GetObjectsComparison { public object FirstObject, SecondObject; public BindingFlags BindingFlagsConditions= BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static; } public struct SetObjectsComparison { public FieldInfo SecondObjectFieldInfo; public dynamic FirstObjectFieldInfoValue, SecondObjectFieldInfoValue; public bool ErrorFound; public GetObjectsComparison GetObjectsComparison; } private static bool ObjectsComparison(GetObjectsComparison GetObjectsComparison) { GetObjectsComparison FunctionGet = GetObjectsComparison; SetObjectsComparison FunctionSet = new SetObjectsComparison(); if (FunctionSet.ErrorFound==false) foreach (FieldInfo FirstObjectFieldInfo in FunctionGet.FirstObject.GetType().GetFields(FunctionGet.BindingFlagsConditions)) { FunctionSet.SecondObjectFieldInfo = FunctionGet.SecondObject.GetType().GetField(FirstObjectFieldInfo.Name, FunctionGet.BindingFlagsConditions); FunctionSet.FirstObjectFieldInfoValue = FirstObjectFieldInfo.GetValue(FunctionGet.FirstObject); FunctionSet.SecondObjectFieldInfoValue = FunctionSet.SecondObjectFieldInfo.GetValue(FunctionGet.SecondObject); if (FirstObjectFieldInfo.FieldType.IsNested) { FunctionSet.GetObjectsComparison = new GetObjectsComparison() { FirstObject = FunctionSet.FirstObjectFieldInfoValue , SecondObject = FunctionSet.SecondObjectFieldInfoValue }; if (!ObjectsComparison(FunctionSet.GetObjectsComparison)) { FunctionSet.ErrorFound = true; break; } } else if (FunctionSet.FirstObjectFieldInfoValue != FunctionSet.SecondObjectFieldInfoValue) { FunctionSet.ErrorFound = true; break; } } return !FunctionSet.ErrorFound; } 

Eine Möglichkeit wäre, Equals() für jeden beteiligten Typ zu überschreiben. Zum Beispiel überschreibt Ihr Top-Level-Objekt Equals() , um die Equals() -Methode aller 5 untergeordneten Objekte aufzurufen. Diese Objekte sollten auch Equals() außer Kraft setzen, sofern sie benutzerdefinierte Objekte sind, usw., bis die gesamte Hierarchie verglichen werden kann, indem einfach eine Gleichheitsprüfung für die Objekte der obersten Ebene durchgeführt wird.

Verwenden Sie IEquatable Interface, das eine Methode Equals .

Ich würde sagen, dass:

Object1.Equals(Object2)

wäre was du suchst. Das ist, wenn Sie schauen, ob die Objekte gleich sind, was Sie zu fragen scheinen.

Wenn Sie überprüfen möchten, ob alle untergeordneten Objekte identisch sind, führen Sie sie mit der Equals() -Methode durch eine Schleife.