2010-04-29 25 views
114

Desidero che la mia classe Food sia in grado di eseguire il test ogni volta che è uguale a un'altra istanza di Food. Lo utilizzerò in seguito contro un elenco e voglio utilizzare il suo metodo List.Contains(). Devo implementare IEquatable<Food> o sostituire solo Object.Equals()? Da MSDN:Qual è la differenza tra IEquatable e l'override di Object.Equals()?

Questo metodo determina uguaglianza utilizzando il confronto uguaglianze predefinito, come definito dal attuazione dell'oggetto del metodo IEquatable.Equals per T (il tipo di valori nell'elenco).

Quindi la mia prossima domanda è: quali funzioni/classi del framework .NET fanno uso di Object.Equals()? Dovrei usarlo in primo luogo?

+2

Una spiegazione molto buona qui http://blogs.msdn.com/b/jaredpar/archive/2009/01/15/if-you-implement-iequatable-t-you-still-must-override-object- s-equals-and-gethashcode.aspx – nawfal

+1

@nawfal: che era già riferito nella risposta accettata. –

+0

possibile duplicato di [Understanding IEquatable] (http://stackoverflow.com/questions/411500/understanding-iequatable) – nawfal

risposta

140

Il motivo principale è la prestazione. Quando i generici sono stati introdotti in .NET 2.0, sono stati in grado di aggiungere una serie di classi pulite come List<T>, Dictionary<K,V>, HashSet<T>, ecc. Queste strutture fanno un uso pesante di GetHashCode e Equals. Ma per i tipi di valore questo ha richiesto il pugilato. IEquatable<T> consente a una struttura di implementare un metodo Equals fortemente digitato in modo che non sia necessaria alcuna box. Quindi prestazioni molto migliori quando si utilizzano tipi di valore con raccolte generiche.

I tipi di riferimento non ne traggono beneficio, ma l'implementazione di IEquatable<T> consente di evitare un cast da System.Object che può fare la differenza se viene chiamato frequentemente.

Come indicato su Jared Parson's blog, è comunque necessario implementare le sostituzioni dell'oggetto.

+0

Esiste un cast tra i tipi di riferimento? Ho sempre pensato che i cast fossero solo "dichiarazioni" che fai al compilatore quando assegni alcuni cast non ovvi da un tipo di oggetto a un altro. Questo, dopo averlo compilato, è che il codice non sapeva nemmeno che c'era un cast lì. –

+5

Questo è vero in C++ ma non in linguaggi .NET che fanno rispettare la sicurezza del tipo. Esiste un cast di esecuzione e se il cast non riesce, viene generata un'eccezione. Quindi c'è una piccola penalità di runtime da pagare per il casting. Il compilatore può ottimizzare gli aggiornamenti via via Ad esempio object o = (oggetto) "stringa"; Ma downcasting: string s = (string) o; - deve avvenire in fase di esecuzione. – Josh

+1

Vedo. Per caso hai un posto dove posso ottenere quel tipo di informazioni "più profonde" su .NET? Grazie! –

34

Secondo i MSDN:

Se si implementa IEquatable(T), si dovrebbe anche ignorare la classe di base implementazioni di Object.Equals(Object) e GetHashCode modo che il loro comportamento è coerente con quella del metodo IEquatable(T).Equals . Se si esegue l'override di Object.Equals(Object), l'implementazione forzata di viene anche chiamata nelle chiamate al metodo statico Equals(System.Object, System.Object) sulla classe. Ciò garantisce che tutte le chiamate del metodo restituiscano consistenti i valori coerenti con .

Quindi sembra che non ci sia una reale differenza funzionale tra i due, tranne che potrebbe essere chiamato a seconda di come viene utilizzata la classe. Dal punto di vista delle prestazioni, è meglio usare la versione generica perché non vi è alcuna penalità di boxing/unboxing ad essa associata.

Da un punto di vista logico, è anche meglio implementare l'interfaccia. Sovrascrivere l'oggetto in realtà non dice a nessuno che la tua classe è effettivamente equa. L'override può essere solo una classe nulla o un'implementazione superficiale. L'uso dell'interfaccia dice esplicitamente: "Ehi, questa cosa è valida per il controllo di uguaglianza!" È solo un design migliore.

+0

Sì, sono entrambi buoni punti. –

+4

Le strutture devono sicuramente implementare iEquatable (of theirOwnType) se devono essere utilizzate come chiavi in ​​un dizionario o collezione simile; offrirà un notevole incremento delle prestazioni. Le classi non ereditabili riceveranno un leggero aumento delle prestazioni implementando IEquatable (of theirOwnType). Le classi ereditabili dovrebbero // non // implementare IEquatable. – supercat

19

Estendere ciò che Josh ha detto con un esempio pratico.+1 a Josh - Stavo per scrivere lo stesso nella mia risposta. Eguali

public abstract class EntityBase : IEquatable<EntityBase> 
{ 
    public EntityBase() { } 

    #region IEquatable<EntityBase> Members 

    public bool Equals(EntityBase other) 
    { 
     //Generic implementation of equality using reflection on derived class instance. 
     return true; 
    } 

    public override bool Equals(object obj) 
    { 
     return this.Equals(obj as EntityBase); 
    } 

    #endregion 
} 

public class Author : EntityBase 
{ 
    public Author() { } 
} 

public class Book : EntityBase 
{ 
    public Book() { } 
} 

In questo modo, ho ri-utilizzabili() metodo che funziona out of the box per tutte le mie classi derivate.

+0

Ancora una domanda. Qual è il vantaggio dell'utilizzo di "obj as EntityBase" anziché di (EntityBase) obj? Solo una questione di stile o c'è qualche vantaggio? –

+15

in caso di "obj as EntityBase" - se obj non è di tipo EntityBase, passerà "null" e continuerà senza errori o eccezioni, ma nel caso di "(EntityBase) obj", tenterà forzatamente di lanciare il obj su EntityBase e se obj non è di tipo EntityBase, genererà InvalidCastException. E sì, "as" può essere applicato solo ai tipi di riferimento. –

+0

Ah, capisco. Grande. Grazie! –

Problemi correlati