È passato molto tempo, ma penso comunque che sia ancora necessario dare una risposta corretta a questa domanda, comprese le spiegazioni sui perché e come. La risposta migliore finora è quella che cita esaustivamente l'MSDN - non provare a stabilire le tue regole, i ragazzi della MS sapevano cosa stavano facendo.
Ma prima le cose: la linea guida citata nella domanda è sbagliata.
Ora i perché - ce ne sono due
Primo perché : se l'hashcode viene calcolato in un modo, non cambia durante la vita di un oggetto, anche se l'oggetto stesso cambia, di quanto non si romperà il contratto uguale.
Ricorda: "Se due oggetti si confrontano come uguali, il metodo GetHashCode per ogni oggetto deve restituire lo stesso valore. Tuttavia, se due oggetti non si confrontano come uguali, i metodi GetHashCode per i due oggetti non devono restituire valori diversi."
La seconda frase viene spesso interpretata erroneamente come "L'unica regola è che al momento della creazione dell'oggetto, l'hashcode di oggetti uguali deve essere uguale". Non so davvero perché, ma qui si trova anche l'essenza della maggior parte delle risposte.
Pensa a due oggetti contenenti un nome, in cui il nome viene utilizzato nel metodo uguale: Stesso nome -> stessa cosa. Crea istanza A: Nome = Joe Crea istanza B: Nome = Pietro
Hashcode A e Hashcode B molto probabilmente non saranno gli stessi. Cosa succederebbe ora, quando il Nome dell'istanza B viene cambiato in Joe?
Secondo le linee guida della domanda, l'hashcode di B non cambierebbe. Il risultato sarebbe: A.Equals (B) ==> true Ma allo stesso tempo: A.GetHashCode () == B.GetHashCode () ==> false.
Ma esattamente questo comportamento è esplicitamente vietato dal contratto uguale e hashcode.
Secondo perché : mentre è - ovviamente - vero, che i cambiamenti nell'hashcode potrebbero rompere gli elenchi hash e altri oggetti usando l'hashcode, anche il contrario è vero. Se non si modifica l'hashcode, nel peggiore dei casi si otterranno elenchi di hash, in cui molti oggetti diversi avranno lo stesso hashcode e quindi si troveranno nello stesso hash bin, ad esempio quando gli oggetti vengono inizializzati con un valore standard.
Ora veniamo ai campi Bene, a prima vista sembra esserci una contraddizione: in entrambi i casi, il codice si romperà. Ma nessuno dei due problemi proviene da hashcode modificato o invariato.
La fonte dei problemi è ben descritta in MSDN:
Dalla voce hashtable di MSDN:
Gli oggetti chiave devono essere immutabili purché siano utilizzati come chiavi nella Hashtable.
Questo significa:
Qualsiasi oggetto che crea un valore hash deve cambiare l'hashvalue, quando l'oggetto cambia, ma non deve - assolutamente non deve - consentire eventuali modifiche a se stesso, quando viene utilizzato all'interno di un Hashtable (o qualsiasi altro oggetto che utilizza Hash, ovviamente) .
Innanzitutto come sarebbe ovviamente il modo più semplice per progettare oggetti immutabili solo per l'uso in hashtable, che verranno creati come copie degli oggetti normali, mutabili quando necessario. All'interno degli oggetti immutabili, è ovviamente ok memorizzare nella cache l'hashcode, poiché è immutabile.
Secondo come O assegnare all'oggetto un "hash hashed now" -flag, assicurarsi che tutti i dati degli oggetti siano privati, controllare il flag in tutte le funzioni che possono cambiare i dati degli oggetti e lanciare i dati di un'eccezione se la modifica non è consentita (cioè il flag è impostato ). Ora, quando metti l'oggetto in qualsiasi area con hash, assicurati di impostare la bandiera e - anche - disinserire la bandiera, quando non è più necessaria. Per facilità d'uso, consiglierei di impostare automaticamente il flag all'interno del metodo "GetHashCode" - in questo modo non può essere dimenticato. E la chiamata esplicita di un metodo "ResetHashFlag" farà in modo che il programmatore debba pensare, sia che sia o non sia autorizzato a modificare i dati degli oggetti ormai.
Ok, cosa si dovrebbe dire anche: ci sono casi in cui è possibile avere oggetti con dati mutabili, in cui l'hashcode è comunque invariato, quando i dati degli oggetti vengono modificati, senza violare il contratto uguale e hashcode.
Ciò richiede tuttavia che il metodo uguale non si basi anche sui dati mutabili. Quindi, se scrivo un oggetto e creo un metodo GetHashCode che calcola un valore una sola volta e lo memorizza all'interno dell'oggetto per restituirlo in chiamate successive, allora devo, ancora: assolutamente, creare un metodo Equals, che utilizzerà valori memorizzati per il confronto, in modo che A.Equals (B) non cambierà mai da falso a vero. Altrimenti, il contratto sarebbe rotto. Il risultato di questo sarà di solito che il metodo Equals non ha alcun senso - non è il riferimento originale uguale, ma non è neppure un valore uguale. A volte, questo può essere un comportamento previsto (ovvero i registri dei clienti), ma di solito non lo è.
Quindi, fai semplicemente cambiare il risultato GetHashCode, quando cambiano i dati degli oggetti, e se è previsto (o solo possibile) l'uso dell'oggetto all'interno dell'hash usando liste o oggetti, allora rendi l'oggetto immutabile o crea un flag di sola lettura da usare per il durata di un elenco con hash contenente l'oggetto.
(A proposito: tutto ciò non è specifico per C # o .NET. È nella natura di tutte le implementazioni hashtable, o più in generale di qualsiasi elenco indicizzato, che l'identificazione dei dati degli oggetti non dovrebbe mai cambiare, mentre l'oggetto è nell'elenco Se si verifica una violazione di questa regola, si verificherà un comportamento imprevisto e imprevedibile. Da qualche parte, potrebbero esserci implementazioni di elenchi che monitorano tutti gli elementi all'interno dell'elenco e reindicizzano automaticamente l'elenco, ma le prestazioni di questi saranno sicuramente alquanto raccapriccianti.)