2012-02-16 19 views
113

Desidero comprendere gli scenari in cui devono essere utilizzati gli standard IEqualityComparer<T> e IEquatable<T>. La documentazione MSDN per entrambi i look è molto simile.Qual è la differenza tra IEqualityComparer <T> e IEquatable <T>?

+3

questa domanda ha bisogno di più upvotes! – nawfal

+1

[MSDN sez:] (https://msdn.microsoft.com/en-us/library/ms132151 (v = vs.110) .aspx) "Questa interfaccia consente l'implementazione del confronto di uguaglianza personalizzato ** per le raccolte. * * "che ovviamente viene dedotto nella risposta selezionata. MSDN raccomanda anche ereditare '' EqualityComparer invece di implementare l'interfaccia "perché' 'EqualityComparer test uguaglianza con' 'IEquatable radarbob

+0

... la suggerisce sopra dovrei creare una collezione personalizzata per qualsiasi' 'T' attuazione IEquatable '. Una raccolta come 'List ' ha qualche sorta di bug sottile in esso altrimenti? – radarbob

risposta

94

IEqualityComparer<T> è un'interfaccia per un oggetto che esegue il confronto su due oggetti del tipo T.

IEquatable<T> è per un oggetto di tipo T in modo che possa confrontarsi con un altro.

+34

+1 _Idable è per un oggetto in modo che possa confrontarsi con un altro_ ** dello stesso tipo ** – gdoron

+0

Semplice, conciso e facilmente comprensibile – Tilak

+0

Ditto @Tilak .... Non è più necessaria alcuna spiegazione – rajibdotnet

3

Uno confronta due T s. L'altro può confrontarsi con altri T s. Di solito, dovrai usarne solo uno alla volta, non entrambi.

9

IEqualityComparer è utilizzabile quando l'uguaglianza di due oggetti viene implementata esternamente, ad es. se si desidera definire un confronto per due tipi per i quali non si dispone della fonte o per i casi in cui l'uguaglianza tra due elementi ha senso solo in un contesto limitato.

IEquatable è per l'oggetto stesso (quello che viene confrontato per l'uguaglianza) da implementare.

+0

Sei l'unico che ha effettivamente sollevato il problema "Plugging in uguaglianza" (utilizzo esterno) più 1. –

16

Hai già la definizione di base di cosa sono. In breve, se si implementa IEquatable<T> sulla classe T, il metodo Equals su un oggetto di tipo T indica se l'oggetto stesso (quello testato per l'uguaglianza) è uguale a un'altra istanza dello stesso tipo T. Considerando che, IEqualityComparer<T> è per testare l'uguaglianza di qualsiasi due istanze di T, in genere al di fuori dell'ambito delle istanze di T.

Per quanto riguarda quello che sono per all'inizio può confondere. Dalla definizione dovrebbe essere chiaro che quindi IEquatable<T> (definito nella classe T) dovrebbe essere lo standard di fatto per rappresentare l'unicità dei suoi oggetti/istanze. HashSet<T>, Dictionary<T, U> (considerando anche che lo GetHashCode è sovrascritto), Contains su List<T> ecc. Fare uso di questo. L'implementazione di IEqualityComparer<T> su T non aiuta i casi generali sopra menzionati. Di conseguenza, c'è poco valore nell'implementazione di IEquatable<T> in qualsiasi altra classe diversa da T. Questo:

class MyClass : IEquatable<T> 

ha senso.

D'altra parte

class T : IEquatable<T> 
{ 
    //override ==, !=, GetHashCode and non generic Equals as well 

    public bool Equals(T other) 
    { 
     //.... 
    } 
} 

è come dovrebbe essere fatto.

IEqualityComparer<T> può essere utile quando è richiesta una convalida personalizzata di uguaglianza, ma non come regola generale. Ad esempio, in una classe di Person a un certo punto potrebbe essere necessario testare l'uguaglianza di due persone in base alla loro età.In questo caso si può fare:

class Person 
{ 
    public int Age; 
} 

class AgeEqualityTester : IEqualityComparer<Person> 
{ 
    public bool Equals(Person x, Person y) 
    { 
     return x.Age == y.Age; 
    } 

    public int GetHashCode(Person obj) 
    { 
     return obj.Age.GetHashCode; 
    } 
} 

per metterli alla prova, provare

var people = new Person[] { new Person { age = 23 } }; 
Person p = new Person() { age = 23 }; 

print people.Contains(p); //false; 
print people.Contains(p, new AgeEqualityTester()); //true 

Allo stesso modo IEqualityComparer<T> su T non ha senso.

class Person : IEqualityComparer<Person> 

Vero che questo funziona, ma non sembra buono per gli occhi e sconfigge la logica.

Di solito quello che ti serve è IEquatable<T>. Inoltre, idealmente, è possibile avere solo uno IEquatable<T> mentre più IEqualityComparer<T> è possibile in base a criteri diversi.

Il IEqualityComparer<T> e IEquatable<T> sono esattamente analoga a Comparer<T> e IComparable<T> che sono utilizzati per scopi di confronto anziché pari; un buon filo here in cui ho scritto la stessa risposta :)

+0

'public int GetHashCode (Person obj)' dovrebbe restituire 'obj.GetHashCode()' –

+0

@HristoYankov dovrebbe restituire 'obj.Age.GetHashCode'. editerà. – nawfal

+0

Si prega di spiegare perché il comparatore deve fare una tale ipotesi su cosa sia l'hash dell'oggetto confrontato? Il tuo comparatore non sa come Person calcola il suo Hash, perché lo sostituiresti? –

36

Al momento di decidere se utilizzare o IEquatable<T>IEqualityComparer<T>, si potrebbe chiedere:

Esiste un modo preferito di testare due istanze di T per l'uguaglianza, o ci sono molti modi ugualmente validi?

  • Se c'è un solo modo di testare due istanze di T per l'uguaglianza, o se uno dei diversi metodi è preferito, quindi IEquatable<T> sarebbe la scelta giusta: si suppone Questa interfaccia deve essere implementata solo da T, in modo che un'istanza di T abbia una conoscenza interna di come confrontarsi con un'altra istanza di T.

  • D'altra parte, se ci sono diversi metodi ugualmente ragionevoli di confrontare due T s per l'uguaglianza, IEqualityComparer<T> sembrerebbe più appropriato: Questa interfaccia non è pensato per essere implementato da T sé, ma da altre classi "esterne" . Pertanto, quando si verificano due istanze di T per l'uguaglianza, poiché T non ha alcuna comprensione interna dell'uguaglianza, sarà necessario effettuare una scelta esplicita di un'istanza IEqualityComparer<T> che esegue il test in base ai propri requisiti specifici.

Esempio:

Consideriamo questi due tipi (che si suppone di avere value semantics):

interface IIntPoint : IEquatable<IIntPoint> 
{ 
    int X { get; } 
    int Y { get; } 
} 

interface IDoublePoint // does not inherit IEquatable<IDoublePoint>; see below. 
{ 
    double X { get; } 
    double Y { get; } 
} 

Perché sarebbe solo uno di questi tipi ereditare IEquatable<>, ma non l'altro ?

In teoria, esiste un solo modo ragionevole di confrontare due istanze di entrambi i tipi: Sono uguali se le proprietà X e Y in entrambe le istanze sono uguali.Secondo questo modo di pensare, entrambi i tipi dovrebbero implementare IEquatable<>, perché non sembra probabile che esistano altri modi significativi per eseguire un test di uguaglianza.

Il problema qui è che comparing floating-point numbers for equality might not work as expected, due to minute rounding errors. Esistono diversi metodi per confrontare i numeri in virgola mobile per quasi-uguaglianza, ognuno con specifici vantaggi e compromessi e si potrebbe voler essere in grado di scegliere da soli quale metodo sia appropriato.

sealed class DoublePointNearEqualityComparerByTolerance : IEqualityComparer<IDoublePoint> 
{ 
    public DoublePointNearEqualityComparerByTolerance(double tolerance) { … } 
    … 
    public bool Equals(IDoublePoint a, IDoublePoint b) 
    { 
     return Math.Abs(a.X - b.X) <= tolerance && Math.Abs(a.Y - b.Y) <= tolerance; 
    } 
    … 
} 

nota che la pagina ho collegato a (sopra) afferma esplicitamente che questo test per quasi l'uguaglianza ha alcuni punti deboli. Poiché si tratta di un'implementazione IEqualityComparer<T>, puoi semplicemente sostituirla se non è abbastanza buona per i tuoi scopi.

+4

Il metodo di confronto suggerito per testare l'uguaglianza in doppio punto è interrotto, poiché qualsiasi implementazione legittima di IEqualityComparer deve * implementare una relazione di equivalenza, il che implica che in * tutti * casi in cui due oggetti sono uguali a un terzo, devono * * confronta uguali tra loro. Qualsiasi classe che implementa 'IEquatable ' o 'IEqualityComparer ' in una maniera contraria a quanto sopra è rotto. – supercat

+3

Un esempio migliore sarebbe il confronto tra stringhe. Ha senso avere un metodo di confronto delle stringhe che consideri le stringhe come uguali solo se contengono la stessa sequenza di byte, ma esistono altri metodi di confronto utili * che costituiscono anche relazioni di equivalenza *, come i confronti insensibili alle maiuscole e minuscole.Si noti che un 'IEqualityComparer ' che considera "Ciao" uguale a "Hello" e "hLoLo" deve considerare "Hello" e "hLoLo" uguali tra loro, ma per la maggior parte dei metodi di confronto non sarebbe un problema. – supercat

+0

@supercat, grazie per il prezioso feedback. È probabile che non riesca a modificare la mia risposta nei prossimi giorni, quindi se lo desideri, ti preghiamo di modificarlo come meglio credi. – stakx

Problemi correlati