2010-12-14 15 views
8

Ich habe eine Klasse enthält einige String-Mitglieder, einige Doppel-Mitglieder und einige Array-Objekte.Vergleichen von Objekten

Ich erstelle zwei Objekte dieser Klasse, gibt es eine einfache, effiziente Möglichkeit, diese Objekte zu vergleichen und ihre Gleichheit zu sagen? Irgendwelche Vorschläge?

Ich weiß, wie man eine Vergleichsfunktion schreibt, aber es wird zeitaufwendig sein.

+2

Wie lauten Ihre Bedingungen für die Gleichheit der Objekte? – BoltClock

+0

Grundsätzlich sollten alle Werte in diesen String-Membern, Double-Membern und dem/den Array (s) übereinstimmen! – siva

+1

möglich duplizieren von http://StackOverflow.com/Questions/506096/ComparingObject-Properties-in-C – RameshVel

Antwort

11

Die einzige Möglichkeit, die Sie wirklich tun können, besteht darin, bool Object.Equals(object other) außer Kraft zu setzen, um wahr zurückzugeben, wenn Ihre Bedingungen für die Gleichheit erfüllt sind, und andernfalls false zurückzugeben. Sie müssen auch int Object.GetHashCode() außer Kraft setzen, um einen von berechneten Wert der Daten zurückgeben, die Sie beim Überschreiben berücksichtigen Equals().

Als Nebenbemerkung beachten Sie, dass der Vertrag für GetHashCode() angibt, dass der Rückgabewert für zwei Objekte gleich sein muss, wenn Equals() beim Vergleich True zurückgegeben wird. Dies bedeutet, dass return 0; eine gültige Implementierung von GetHashCode() ist, aber es führt zu Ineffizienzen, wenn Objekte Ihrer Klasse als Wörterbuchschlüssel verwendet oder in einer HashSet<T> gespeichert werden.

So wie ich Gleichheit implementieren ist wie folgt:

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. 
    } 
} 

Ein einfacher Weg GetHashCode() der Implementierung besteht darin, die Hash-Codes aller Daten, die Sie in Equals() für Gleichheit betrachten XOR. Wenn also zum Beispiel die Eigenschaften, die Sie für die Gleichstellung vergleichen string FirstName; string LastName; int Id; sind, können Sie Ihre Implementierung wie folgt aussehen:

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 
} 

ich in der Regel nicht haben Vorrang vor den Gleichheitsoperatoren, wie die meiste Zeit ich bin besorgt darüber, mit Gleichheit nur für die Zwecke von Wörterbuchschlüsseln oder Sammlungen. Ich würde nur in Betracht ziehen, die Gleichheitsoperatoren zu überschreiben, wenn Sie wahrscheinlich mehr Vergleiche nach Wert als nach Verweis durchführen, da es syntaktisch weniger ausführlich ist. Sie müssen jedoch daran denken, alle Stellen zu ändern, an denen Sie == oder != für Ihr Objekt (einschließlich Ihrer Implementierung von Equals()!) Verwenden, um Object.ReferenceEquals() zu verwenden oder beide Operanden in object zu konvertieren. Dieses fiese gotcha (das endlose Rekursion in Ihrem Gleichheitstest verursachen kann, wenn Sie nicht aufpassen) ist einer der Hauptgründe, die ich selten diese Operatoren überschreibe.

1

Die beste Antwort ist, IEquatable für Ihre Klasse zu implementieren - es ist möglicherweise nicht die Antwort, die Sie hören möchten, aber das ist der beste Weg, value equivalence in .NET zu implementieren.

Eine andere Möglichkeit wäre die Berechnung einen eindeutigen Hash von allen Mitgliedern der Klasse und dann gegen diejenigen tun Wertvergleiche, aber das ist noch mehr Arbeit als eine Vergleichsfunktion zu schreiben;)

+0

Der Hash-Ansatz ist auch anfällig für Fehlalarme. – cdhowie

+0

Ja, ich kann es nicht empfehlen. – Aaronontheweb

9

Die ‚richtige‘ Art und Weise zu tun Sie es in .NET ist die IEquatable Schnittstelle für Ihre Klasse zu implementieren:

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; 
    } 
} 

Allerdings, wenn Sie wirklich richtig es tun wollen, this isn't all you should do.Sie sollten auch die Methoden Equals (object, object) und GetHashCode (object) überschreiben, so dass, egal wie Ihr aufrufender Code die Gleichheit vergleicht (vielleicht in einem Dictionary oder vielleicht in einer lose typisierten Sammlung), Ihr Code und nicht Referenz- Typ Gleichheit wird der entscheidende Faktor sein:

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

Da diese Objekte sind meine Vermutung ist, dass Sie die Equals-Methode für Objekte überschreiben müssen. Andernfalls wird die Equals-Methode nur dann in Ordnung sein, wenn beide Objekte auf dasselbe Objekt verweisen.

Ich weiß, das ist nicht die Antwort, die Sie wollen. Da jedoch in Ihrer Klasse nur wenige Eigenschaften vorhanden sind, können Sie die Methode leicht überschreiben.

5

Nur verbrachte den ganzen Tag schreiben eine Erweiterung Methode Schleifen durch Reflektieren über Eigenschaften eines Objekts mit verschiedenen komplexen Bits der Logik, um mit verschiedenen Eigenschaftstyp umzugehen und tatsächlich kam es nahe an gut, dann um 16:55 dämmerte es mir dass, wenn man die beiden Objekt serialisiert, müssen Sie einfach die beiden Strings vergleichen ... duh

hier so ist eine einfache Serializer Erweiterung Methode, die auf Wörterbücher

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(); 
      } 
     } 
} 

Jetzt Ihren Test funktioniert auch so einfach sein kann wie

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

Haben noch nicht ausgiebig getestet, aber sieht vielversprechend und vor allem einfach aus. So oder so, immer noch eine nützliche Methode, um in Ihrem Dienstprogramm richtig eingerichtet zu haben?

+0

Wenn Ihr Objekt jedoch String-Eigenschaften enthält, wird der Vergleich nicht durch Groß-/Kleinschreibung unterbrochen? Ich meine, "Vereinigte Staaten" wären ungleich "Vereinigte Staaten". –

Verwandte Themen