2009-03-04 1 views
6

Wenn ich Equals und GetHashCode überschreibe, wie kann ich entscheiden, welche Felder verglichen werden sollen? Und was passiert, wenn ich zwei Objekte mit jeweils zwei Feldern habe, aber Equals nur ein Feld überprüft?Überschreiben Gleich(), aber nicht alle Felder prüfen - was wird passieren?

Mit anderen Worten, lassen Sie uns sagen, dass ich diese Klasse haben:

class EqualsTestClass 
{ 
    public string MyDescription { get; set; } 
    public int MyId { get; set; } 

    public override bool Equals(object obj) 
    { 
     EqualsTestClass eq = obj as EqualsTestClass; 
     if(eq == null) { 
      return false; 
     } else { 
      return MyId.Equals(eq.MyId); 
     } 
    } 

    public override int GetHashCode() 
    { 
     int hashcode = 23; 
     return (hashcode * 17) + MyId.GetHashCode(); 
    } 
} 

ich zwei Objekte gleich prüfen, ob sie die gleiche myid haben. Wenn also die Id gleich ist, aber die Beschreibung unterschiedlich ist, werden sie immer noch als gleich angesehen.

Ich frage mich nur, was die Fallen dieses Ansatzes sind? Natürlich verhält sich erwartet ein Konstrukt, wie dies zum Beispiel:

 List<EqualsTestClass> test = new List<EqualsTestClass>(); 

     EqualsTestClass eq1 = new EqualsTestClass(); 
     eq1.MyId = 1; 
     eq1.MyDescription = "Des1"; 

     EqualsTestClass eq2 = new EqualsTestClass(); 
     eq2.MyId = 1; 
     eq2.MyDescription = "Des2"; 

     test.Add(eq1); 
     if (!test.Contains(eq2)) 
     { 
      // Will not be executed, as test.Contains is true 
      test.Add(eq2); 
     } 

Als EQ2 auf EQ1 wert gleich ist, wird er nicht hinzugefügt werden. Aber das ist Code, den ich kontrolliere, aber ich frage mich, ob es Code im Framework gibt, der unerwartete Probleme verursachen könnte?

Also, sollte ich immer alle öffentlichen Felder in meinem Equals() Vergleich hinzufügen, oder was sind die Richtlinien, um eine böse Überraschung wegen einiger schlechter Framework-Mojo zu vermeiden, die völlig unerwartet war?

Antwort

11

Der Grund für das Überschreiben Equals() ist, dass Sie definieren, was es für zwei Instanzen bedeutet, gleich zu sein. In einigen Fällen bedeutet dies, dass alle Felder gleich sein müssen, aber nicht müssen. Du entscheidest. Weitere Informationen finden Sie unter documentation und post.

+0

vereinbart, wie die OP, ich habe außer Kraft gesetzt Equals(), um nur zu überprüfen eine primäre ID, und das ist es, es ist völlig die Programmierer rufen. –

1

Ich denke nicht, dass Sie sich in diesem Fall über das Framework sorgen müssen. Wenn Sie als Klassen-Designer zwei Instanzen dieser Klasse als gleich betrachten, wenn sie dieselbe MyId verwenden, müssen Sie MyId nur in den Methoden equals() und GetHashCode() überschreiben.

1

Sie müssen nur nach den Feldern suchen, die übereinstimmen müssen, wenn alles, was übereinstimmen muss, die ID ist, dann gehen Sie damit.

1

Eine Frage: Wenn ich Equals und GetHashCode überschreibe, wie entscheide ich, welche Felder ich vergleiche?

Es hängt davon ab, was Sie zu erreichen versuchen. Wenn Sie versuchen zu sehen, ob die Objekte genau gleich sind, sollten Sie alle vergleichen. Wenn Sie einen "Schlüssel" haben und Sie nur wissen wollen, ob sie das gleiche 'Objekt' sind, auch wenn andere Daten unterschiedlich sind, dann überprüfen Sie einfach die 'Schlüssel' Werte.

Und was passiert, wenn ich zwei Objekte mit jeweils zwei Feldern habe, aber Equals nur ein Feld überprüft?

Dann werden Sie eine Gleichheits Methode, die prüft nur, wenn Sie die Taste ‚‘ das gleiche ist, und möglicherweise mehrere ‚gleich‘ Objekte haben könnten, die internen Abweichungen aufweisen.

1

Andere haben gesagt, das ist perfekt gültig und erwartet, und es ist genau so, wie Equals funktionieren soll. Es gibt also kein Problem als Klasse.

Ich würde sehr vorsichtig als API sein. Verzeihen Sie, wenn es nicht beabsichtigt ist: In diesem Fall ist das nur eine Warnung an andere.

Das potentielle Problem ist, dass Benutzer der API natürlich erwarten, dass gleiche Objekte "gleich" sind. Dies ist nicht Teil des Gleichheitsvertrags, aber es ist Teil des gesunden Menschenverstandes des Wortes. Die Klasse sieht ein bisschen wie ein binäres Tupel aus, ist aber keins, also sollte das aus vernünftigen Gründen sein.

Ein Beispiel für solch einen vernünftigen Grund ist, dass ein Feld ein "sichtbares Implementierungsdetail" ist, wie der maximale Ladefaktor auf einem Hashtabellen-basierten Container. Ein Beispiel für einen riskanten (wenn auch verführerischen) Grund ist, "weil ich die Beschreibung später hinzugefügt habe und die Equals-Methode nicht ändern wollte, falls sie etwas kaputt gemacht hat".

Es ist also absolut richtig etwas etwas kontraintuitiv zu machen, besonders wenn Sie eindeutig dokumentieren, dass das Verhalten überraschend ist. Solche Equals-Methoden müssen unterstützt werden, da ein Verbot in Fällen, in denen ein Feld offensichtlich irrelevant ist, verrückt wäre. Aber es sollte klar sein, warum es sinnvoll ist, zwei ID-Beschreibungs-Paare mit derselben ID und unterschiedlichen Beschreibungen zu erstellen, aber es macht keinen Sinn, sie beide einem Container hinzuzufügen (wie HashSet), der Equals/HashCode verwendet, um Duplikate zu verhindern Einträge.

0

Ihr Beispiel überprüft, ob eine Liste (von EqualsTestClass) ein Objekt desselben Typs mit denselben Eigenschaftswerten enthält. Eine andere Möglichkeit, diese Aufgabe zu erledigen, ohne Gleichheitszeichen zu überschreiben (und das traditionelle Verständnis von Gleichgestellten), besteht darin, einen benutzerdefinierten Vergleicher zu verwenden. Es würde wie folgt aussehen (in VB):

Public Class EqualsTestComparer 
Implements IEqualityComparer(Of EqualsTestClass) 
Public Function Equals1(ByVal x As EqualsTestClass, ByVal y As EqualsTestClass) As Boolean Implements System.Collections.Generic.IEqualityComparer(Of EqualsTestClass).Equals 
    If x.MyId = y.MyId and x.MyDescription = y.MyDescription Then 
     Return True 
    Else 
     Return False 
    End If 
End Function 

Public Function GetHashCode1(ByVal obj As EqualsTestClass) As Integer Implements System.Collections.Generic.IEqualityComparer(Of EqualsTestClass).GetHashCode 
    Return obj.ToString.ToLower.GetHashCode 
End Function  
End Class 

Dann in Ihrer Routine verwenden Sie einfach den benutzerdefinierten Vergleich:

If Not test.Contains(eq2, New EqualsTestComparer) Then 
    //Do Stuff 
End if 
Verwandte Themen