2013-06-16 5 views
11

Wenn ich Wörterbücher manchmal verwende, muss ich die Standard Equals Bedeutung ändern, um Schlüssel zu vergleichen. Ich sehe, dass wenn ich die Equals und GetHashCode für die Klasse des Schlüssels überschreibe oder ich eine neue Klasse erzeuge, die IEqualityComparer implementiert, habe ich das gleiche Ergebnis. Was ist der Unterschied zwischen IEqualityComparer und Equals/GethashCode Override? Zwei Beispiele:Was ist der Unterschied zwischen der Verwendung von IEqualityComparer und Equals/GethashCode Override?

class Customer 
{ 
    public string name; 
    public int age; 
    public Customer(string n, int a) 
    { 
     this.age = a; 
     this.name = n; 
    } 
    public override bool Equals(object obj) 
    { 
     Customer c = (Customer)obj; 
     return this.name == c.name && this.age == c.age; 
    } 
    public override int GetHashCode() 
    { 
     return (this.name + ";" + this.age).GetHashCode(); 
    } 
} 
    class Program 
{ 
    static void Main(string[] args) 
    { 
     Customer c1 = new Customer("MArk", 21); 
     Customer c2 = new Customer("MArk", 21); 
     Dictionary<Customer, string> d = new Dictionary<Customer, string>(); 
     Console.WriteLine(c1.Equals(c2)); 
     try 
     { 
      d.Add(c1, "Joe"); 
      d.Add(c2, "hil"); 
      foreach (KeyValuePair<Customer, string> k in d) 
      { 
       Console.WriteLine(k.Key.name + " ; " + k.Value); 
      } 
     } 
     catch (ArgumentException) 
     { 
      Console.WriteLine("Chiave già inserita in precedenza"); 
     } 
     finally 
     { 
      Console.ReadLine(); 
     } 
    } 
} 

}

Zweite ein:

class Customer 
{ 
    public string name; 
    public int age; 
    public Customer(string n, int a) 
    { 
     this.age = a; 
     this.name = n; 
    } 
} 
class DicEqualityComparer : EqualityComparer<Customer> 
{ 
    public override bool Equals(Customer x, Customer y) // equals dell'equalitycomparer 
    { 
     return x.name == y.name && x.age == y.age; 
    } 
    public override int GetHashCode(Customer obj) 
    { 
     return (obj.name + ";" + obj.age).GetHashCode(); 
    } 
} 
class Program 
{ 
    static void Main(string[] args) 
    { 
     Customer c1 = new Customer("MArk", 21); 
     Customer c2 = new Customer("MArk", 21); 
     DicEqualityComparer dic = new DicEqualityComparer(); 
     Dictionary<Customer, string> d = new Dictionary<Customer, string>(dic); 
     Console.WriteLine(c1.Equals(c2)); 
     try 
     { 
      d.Add(c1, "Joe"); 
      d.Add(c2, "hil"); 
      foreach (KeyValuePair<Customer, string> k in d) 
      { 
       Console.WriteLine(k.Key.name + " ; " + k.Value); 
      } 
     } 
     catch (ArgumentException) 
     { 
      Console.WriteLine("Chiave già inserita in precedenza"); 
     } 
     finally 
     { 
      Console.ReadLine(); 
     } 
    } 
} 

}

Beide Beispiele haben das gleiche Ergebnis.

Vielen Dank im Voraus.

+3

Mögliche dupe/ähnlich: http://stackoverflow.com/questions/7751170/why-we-need-the-iequalitycomparer-iequalitycomparert-interface – Clint

+4

Da gibt es mehr als eine Möglichkeit, einige Objekte zu vergleichen. – Pragmateek

Antwort

9

Wenn Sie Equals und GetHashCode überschreiben, ändern Sie die Art und Weise, wie das Objekt feststellen wird, ob es einem anderen entspricht. Und ein Hinweis, wenn Sie Objekte mit dem Operator == vergleichen, wird es nicht das gleiche Verhalten wie Equals haben, es sei denn, Sie überschreiben auch den Operator.

Dadurch haben Sie das Verhalten für eine einzelne Klasse geändert, was ist, wenn Sie die gleiche Logik für andere Klassen benötigen? Wenn Sie einen "generischen Vergleich" brauchen. Deshalb haben Sie IEqualityComparer.

Schauen Sie sich das Beispiel:

interface ICustom 
{ 
    int Key { get; set; } 
} 
class Custom : ICustom 
{ 
    public int Key { get; set; } 
    public int Value { get; set; } 
} 
class Another : ICustom 
{ 
    public int Key { get; set; } 
} 

class DicEqualityComparer : IEqualityComparer<ICustom> 
{ 
    public bool Equals(ICustom x, ICustom y) 
    { 
     return x.Key == y.Key; 
    } 

    public int GetHashCode(ICustom obj) 
    { 
     return obj.Key; 
    } 
} 

Ich habe zwei verschiedene Klassen, können beide den gleichen Vergleich verwenden.

var a = new Custom { Key = 1, Value = 2 }; 
var b = new Custom { Key = 1, Value = 2 }; 
var c = new Custom { Key = 2, Value = 2 }; 
var another = new Another { Key = 2 }; 

var d = new Dictionary<ICustom, string>(new DicEqualityComparer()); 

d.Add(a, "X"); 
// d.Add(b, "X"); // same key exception 
d.Add(c, "X"); 
// d.Add(another, "X"); // same key exception 

Beachten Sie, dass ich nicht Equals, GetHashCode in keiner der Klassen außer Kraft zu setzen hatte. Ich kann diesen Vergleicher in jedem Objekt verwenden, das ICustom implementiert, ohne die Vergleichslogik neu schreiben zu müssen. Ich kann auch eine IEqualityComparer für eine "Elternklasse" machen und auf Klassen verwenden, die erben. Ich kann einen Vergleicher haben, der sich auf eine andere Weise verhält, ich kann einen zum Vergleich Value anstelle von Key machen.

So ermöglicht IEqualityComparer mehr Flexibilität und Sie können generische Lösungen implementieren.

+8

Einfach gesagt, IEqualityComparer externalisiert die Vergleichslogik während Overriding Equals/GetHashCode internalisiert sie - Das gleiche Prinzip/Unterschied für IComparable (internalisieren) und IComparer (Externalize). – h9uest

+1

@ h9uest: gut gesagt. Ich frage mich, ob IEqualityComparer auch internalisiert werden kann. Equals/GetHashCode vereinheitlicht nicht nur die Vergleichslogik, sondern globalisiert sie auch. Es könnte Fälle geben, in denen ich einen internen Vergleich (ohne Sammlung) nur einmal durchführen möchte. – liang

+0

@liang Ich fürchte, IEqualityComparer wurde entwickelt, um Vergleiche zu externalisieren. Normalerweise würde ich MyCustomeComparer schreiben, das IEqualityComparer implementiert und ein MyCustomeComparer-Objekt an die Objekte weitergibt, die es benötigen - ich bin mir sicher, dass Ihnen diese Verwendung bekannt ist. – h9uest

1

Es ist im Wesentlichen das gleiche für diesen Zweck mit einem feinen Unterschied. In Ihrem ersten Beispiel überschreiben Sie Equals mit einem Parameter vom Typ Object und müssen es dann an den Kunden übergeben. In Ihrem zweiten Beispiel können Sie jedoch den Parameter vom Typ Customer verwenden, was bedeutet, dass keine Umwandlung erforderlich ist.

Dies bedeutet, dass das Überschreiben von Equals den Vergleich zwischen zwei Objekten verschiedener Typen (die unter bestimmten Umständen erforderlich sein können) ermöglicht. Die Implementierung von IEqualityComparer bietet jedoch nicht diese Freiheit (die auch unter bestimmten Umständen erforderlich sein kann).

3

Die Objekt Equals() anf GetHashCode() implementieren das Konzept der Gleichheit im Objekt. Möglicherweise möchten Sie jedoch alternative Gleichheitskonzepte verwenden, z. B. einen Gleichheitsvergleich für Adressobjekte, der nur die Postleitzahl und nicht die vollständige Adresse verwendet.

1

Es gibt viele Fälle, in denen man eine Dictionary Objekte mit etwas anderem als 100% Äquivalenz suchen lassen möchte. Als ein einfaches Beispiel kann man wünschen, ein Wörterbuch zu haben, das in Groß- und Kleinschreibung übereinstimmt. Eine Möglichkeit, dies zu erreichen, wäre, Zeichenfolgen in eine kanonische Großbuchstabenform zu konvertieren, bevor sie im Wörterbuch gespeichert oder eine Suche durchgeführt werden. Ein alternativer Ansatz besteht darin, das Wörterbuch mit einer IEqualityComparer<string> zu versorgen, die Hash-Codes berechnet und in einer Art fallunabhängiger Funktion auf Gleichheit prüft. Es gibt einige Umstände, bei denen das Konvertieren von Strings in die kanonische Form und das Verwenden dieses Forms wann immer möglich effizienter ist, aber es gibt andere, bei denen es effizienter ist, die Strings nur in ihrer ursprünglichen Form zu speichern. Eine Funktion, die .NET hätte, die die Nützlichkeit solcher Wörterbücher verbessern würde, wäre ein Mittel zum Anfordern des eigentlichen Schlüsselobjekts, das einem gegebenen Schlüssel zugeordnet ist (wenn also das Wörterbuch die Zeichenfolge "WowZo" als Schlüssel enthält, könnte man "wowzo" nachschlagen und erhalten "WowZo"; die einzige Möglichkeit, ein tatsächliches Schlüsselobjekt abzurufen, wenn TValue keine redundante Referenz darauf enthält, besteht darin, die gesamte Auflistung aufzuzählen. Ein anderes Szenario, in dem es nützlich sein kann, eine alternative Vergleichsmöglichkeit zu haben, ist, wenn ein Objekt einen Verweis auf eine Instanz eines veränderbaren Typs enthält, diese Instanz jedoch niemals mit irgendetwas in Berührung bringt, das sie mutieren könnte. Im Allgemeinen sind zwei Instanzen von int[], die die gleiche Sequenz von Werten enthalten, nicht austauschbar, da es möglich wäre, dass in Zukunft einer oder beide von ihnen geändert werden könnten, um andere Werte zu halten. Auf der anderen Seite, wenn ein Wörterbuch verwendet wird, um int[] Werte zu halten und nachzuschlagen, von denen jeder der einzige Verweis irgendwo im Universum auf eine Instanz von int[] ist, und wenn keine der Instanzen modifiziert oder nach außen ausgesetzt wird Code kann es sinnvoll sein, gleiche Array-Instanzen zu betrachten, die identische Wertefolgen enthalten. Da Array.Equals Tests auf strikte Äquivalenz (Referenzgleichheit) sind, wäre es notwendig, andere Mittel zum Testen der Arrays auf Äquivalenz zu verwenden.

Verwandte Themen