2011-09-05 14 views
10

Ciao Ho una classe con 6 proprietà stringa. Un oggetto univoco avrà valori diversi per almeno uno di questi campiC#. NET Funzione GetHashCode domanda

Per implementare la funzione GetHashCode di IEqualityComparer, concatenare tutte e 6 le proprietà e chiamare il GetHashCode sulla stringa risultante.

ho avuto i seguenti dubbi:

  1. E 'necessario chiamare il GetHashCode su un valore unico?
  2. L'operazione di concatenazione sulle sei proprietà rende il confronto lento?
  3. Devo usare un altro approccio?
+0

Are stai pianificando di confrontare i tuoi oggetti da qualche parte come ordinarli in un array o qualcosa del genere?Questo cambierà sia che tu abbia bisogno di implementare GetHashCode – mydogisbox

+0

ciao mydogisbox, lo sto usando per il metodo List.Contains e passando l'oggetto comparatore ad esso. Ho già implementato Equals e non conosco il giusto approccio per GetHashcode – ganeshran

risposta

3

GetHashCode non è necessario restituire valori non uguali per oggetti "non uguali". Ha solo bisogno di restituire valori uguali per oggetti uguali (deve anche restituire lo stesso valore per la durata dell'oggetto).

Questo significa che:

  1. se due oggetti risultano uguali con Equals, allora la loro GetHashCode deve restituire lo stesso valore.
  2. Se alcune delle 6 proprietà stringa non sono strettamente di sola lettura, non possono prendere parte all'implementazione GetHashCode.

Se non riesci a soddisfare entrambi i punti allo stesso tempo, dovresti rivalutare il tuo design perché qualsiasi altra cosa lascerà la porta aperta ai bug.

Infine, probabilmente è possibile effettuare GetHashCode più veloce chiamando GetHashCode su ciascuna delle 6 stringhe e quindi integrando tutti i 6 risultati in un valore utilizzando alcune operazioni bit a bit.

+0

Ciao Jon, nessuna delle mie proprietà è in sola lettura. Tuttavia, tutti hanno setter privati, anche se possono essere modificati solo dal costruttore. Ciò influirà sul loro utilizzo in GetHashCode? – ganeshran

+0

@ganeshran: Quindi sono effettivamente di sola lettura per tutta la durata dell'oggetto (ad esempio potrebbero essere implementati con i campi di backup 'readonly' se lo si desidera), che è sufficiente. Starai bene – Jon

+0

@Jon Leggendo le condizioni per l'implementazione 'GetHashCode()', vedo che si tratta di un problema con requisiti in conflitto. In effetti, sei solo in parte corretto. I veri requisiti per il metodo 'GetHashCode()' sono: 1. Dovrebbe restituire gli stessi valori per oggetti uguali. 2. Dovrebbe restituire lo stesso valore per lo stesso oggetto mentre non è modificato. 3. Il GetHashCode() dovrebbe essere veloce. D'accordo, ci sono alcuni altri consigli per questo. Ad esempio questo: per le migliori prestazioni, una funzione di hash dovrebbe generare una distribuzione casuale per tutti gli input. –

3

GetHashCode() deve restituire lo stesso codice hash per tutti gli oggetti che restituiscono true se si chiama Equals() su tali oggetti. Ciò significa, ad esempio, che è possibile restituire zero come codice hash indipendentemente dai valori dei campi. Ma ciò renderebbe il tuo oggetto molto inefficiente se conservato in strutture di dati come le tabelle hash.

La combinazione delle stringhe è un'opzione, ma si noti che è possibile ad esempio combinare solo due delle stringhe per il codice hash (mentre si confrontano ancora tutte le stringhe in eguali!).

È inoltre possibile combinare gli hash delle sei stringhe separate anziché calcolare un singolo hash per una stringa combinata. Vedi ad esempio Quick and Simple Hash Code Combinations

Non sono sicuro se sarà molto più veloce della concatenazione della stringa.

+0

Grazie Anders, lo sto usando solo per il confronto dei metodi Contiene. Se combino solo due stringhe per l'hashcode, gli hashcode non sarebbero uguali se i due valori negli oggetti fossero uguali? Questo incasina i confronti, o il GetHashCode non ha alcun effetto sul confronto stesso, e influenza solo la performance – ganeshran

+1

Altri hanno già fatto questo punto, ma mi piacerebbe affrontarlo in un modo diverso perché sembra che la tua intuizione sia incollato. Osserva che GetHashCode() restituisce un int, che può assumere solo 2^32 valori diversi. Il tuo oggetto, composto da 6 stringhe di lunghezza arbitraria, può ovviamente assumere un numero molto elevato di valori. Attraverso questo esempio possiamo facilmente vedere che GetHashCode() non può essere un valore univoco per tutti i possibili valori del tuo oggetto. Deve solo soddisfare questa proprietà: "se a.Equals (b) then a.GetHashCode() == b.GetHashCode()"; e notare che il "se" NON va in entrambe le direzioni. –

+1

Considerazioni pratiche per GetHashCode() sono che sia "buono" e "veloce". Per renderlo "veloce" proverei a evitare tutta l'allocazione della memoria e la copia delle stringhe; renderlo "buono" è un argomento di qualche sfumatura, ma in pratica è sufficiente scambiare insieme i valori GetHashCode() degli oggetti secondari come suggerisce @Jon. Pubblicherò il suggerimento di ReSharper come una "risposta" in modo da poter ottenere la formattazione del codice. –

4

Se i campi stringa sono chiamati af e noto di non essere nulla, questa è la proposta di ReSharper per il vostro GetHashCode()

public override int GetHashCode() { 
    unchecked { 
    int result=a.GetHashCode(); 
    result=(result*397)^b.GetHashCode(); 
    result=(result*397)^c.GetHashCode(); 
    result=(result*397)^d.GetHashCode(); 
    result=(result*397)^e.GetHashCode(); 
    result=(result*397)^f.GetHashCode(); 
    return result; 
    } 
} 
+0

Grazie, userò questo codice. – ganeshran

+0

Vedere la mia risposta qui: http://stackoverflow.com/a/34006336/1911540 –