2009-06-07 10 views
8

Ogni volta che scrivo una classe di dati, di solito passo così tanto tempo a scrivere l'implementazione IEquatable.Uguali agli helper per l'implementazione del metodo (C#)

L'ultima classe che ho scritto era qualcosa di simile:

public class Polygon 
{ 
    public Point[] Vertices { get; set; } 
} 

Implementazione IEquatable era esaustiva. Sicuramente C# 3.0/LINQ aiuta molto, ma i vertici possono essere spostati e/o nell'ordine inverso, e questo aggiunge molta complessità al metodo Equals. Dopo molti test unitari e l'implementazione corrispondente, ho rinunciato e ho cambiato la mia domanda per accettare solo triangoli, che per l'implementazione IEquatable richiedevano solo 11 test unitari per essere completamente coperti.

C'è qualche strumento o tecnica che aiuta a implementare Equals e GetHashCode?

+0

Ti sembra di non essere alla ricerca di Equals generazione di codice, ma per un realizzatore algoritmo generico. Penso che tu sia sfortunato qui. – erikkallen

risposta

8

Uso ReSharper per generare membri di uguaglianza. Opzionalmente implementerà IEquatable<T> e sostituirà gli operatori se lo si desidera (cosa che ovviamente non si fa mai, ma è comunque bello).

L'implementazione di Equals include un override di Object.Equals(Object), nonché una variante fortemente tipizzata (che può evitare il controllo di tipo non necessario). La versione meno tipizzata chiama quella fortemente tipizzata dopo aver eseguito un controllo di tipo. La versione fortemente tipizzata esegue un controllo di uguaglianza di riferimento (Object.ReferenceEquals(Object,Object)) e quindi confronta i valori di tutti i campi (beh, solo quelli che si dice al generatore di includere).

Per quanto riguarda GetHashCode, una fattorizzazione intelligente GetHashCode valori del campo sono combinati (usando unchecked per evitare eccezioni di overflow se si utilizza l'opzione del compilatore checked). Ciascuno dei valori del campo (a parte il primo) viene moltiplicato per i numeri primi prima di essere combinato. È anche possibile specificare quali campi non sarebbero mai nulli e non genererà alcun controllo Null.

Ecco quello che si ottiene per la vostra classe Polygon premendo ALT+Insert quindi selezionando "Genera uguaglianza Utenti":

public class Polygon : IEquatable<Polygon> 
{ 
    public Point[] Vertices { get; set; } 

    public bool Equals(Polygon other) 
    { 
     if (ReferenceEquals(null, other)) return false; 
     if (ReferenceEquals(this, other)) return true; 
     return Equals(other.Vertices, Vertices); 
    } 

    public override bool Equals(object obj) 
    { 
     if (ReferenceEquals(null, obj)) return false; 
     if (ReferenceEquals(this, obj)) return true; 
     if (obj.GetType() != typeof (Polygon)) return false; 
     return Equals((Polygon) obj); 
    } 

    public override int GetHashCode() 
    { 
     return (Vertices != null ? Vertices.GetHashCode() : 0); 
    } 
} 

Alcune delle caratteristiche di cui ho parlato sopra non si applicano in quanto v'è un solo campo . Nota anche che non ha controllato il contenuto dell'array.

In generale, tuttavia, ReSharper rilascia molto codice eccellente in pochi secondi. E quella caratteristica è piuttosto bassa nella mia lista di cose che rende ReSharper uno strumento così straordinario.

+0

Non è magico ma sicuramente aiuta! Grazie –

+2

L'override uguale a (obj) non mi sembra un codice eccellente. Non sarebbe più naturale dire semplicemente return this.Equals (obj as Polygon) ;? – mquander

+0

Codice forse più semplice da leggere, sì. Ma le caratteristiche delle prestazioni sarebbero peggiori. Il JIT ridurrà la prima riga a una semplice istruzione di codice macchina (cmp con zero) che sarà molto veloce da eseguire.La seconda riga si riduce a un'altra semplice istruzione di codice macchina (puntatori di obj cmp) che sarà anche molto veloce da eseguire. La terza riga esegue un controllo del tipo (il JIT lo ottimizza fino a un altro controllo del puntatore basato sul codice del tipo dell'oggetto), e solo alla fine, se i controlli precedenti non riescono, viene effettivamente chiamato Equals. –

2

Per il confronto di due array di elementi, utilizzo il metodo di estensione SequenceEqual.

Come per un generico Equals e GetHashCode, esiste una tecnica basata sulla serializzazione che potrebbe funzionare per voi.

Using MemoryStream and BinaryFormatter for reuseable GetHashCode and DeepCopy functions

+0

Grazie per averlo aggiunto al mio set di strumenti. –

+0

Non riesco a trovare SequenceEqual su 3.5/System.Core.dll, sai dov'è? Sto usando Reflector di Red Gate per visualizzare il codice sorgente. –

+0

Ottimo articolo! Ora mi sono reso conto che scrivere l'implementazione di Equals è stupido, tutto ciò che devi fare è implementare GetHashCode e quindi usarlo su Equals. –

Problemi correlati