Domanda

Ho letto circa 10 domande diverse su quando e come eseguire l'override GetHashCode ma c'è ancora qualcosa che non capisco.La maggior parte delle implementazioni di GetHashCode si basano sui codici hash dei campi dell'oggetto, ma è stato citato che il valore di GetHashCode non dovrebbe mai cambiare nel corso della vita dell'oggetto.Come funziona se i campi su cui si basa sono mutabili?Inoltre, cosa succede se voglio che le ricerche nel dizionario, ecc., siano basate sull'uguaglianza dei riferimenti e non sulla mia sovrascrittura Equals?

Principalmente sto sovrascrivendo Equals per facilità di test unitario, il mio codice di serializzazione che presumo che la serializzazione e la deserializzazione (in XML nel mio caso) uccida l'uguaglianza di riferimento, quindi voglio assicurarmi che almeno sia corretto per uguaglianza di valore.È una cattiva pratica ignorare? Equals in questo caso?Fondamentalmente nella maggior parte del codice in esecuzione voglio l'uguaglianza dei riferimenti e la utilizzo sempre == e non lo ignorerò.Dovrei semplicemente creare un nuovo metodo ValueEquals o qualcosa invece di sovrascrivere Equals?Supponevo che il framework utilizzi sempre == e non Equals per confrontare le cose e quindi ho pensato che fosse sicuro ignorare Equals dal momento che mi sembrava che il suo scopo fosse quello di voler avere una seconda definizione di uguaglianza diversa da quella == operatore.Dalla lettura di molte altre domande, però, sembra che non sia così.

MODIFICARE:

Sembra che le mie intenzioni non fossero chiare, quello che voglio dire è che il 99% delle volte voglio la semplice vecchia uguaglianza di riferimento, comportamento predefinito, nessuna sorpresa.Per casi molto rari voglio avere l'uguaglianza dei valori e voglio richiedere esplicitamente l'uguaglianza dei valori utilizzando .Equals invece di ==.

Quando lo faccio, il compilatore mi consiglia di eseguire l'override GetHashCode così, ed è così che è nata questa domanda.Sembrava che ci fossero obiettivi contraddittori GetHashCode quando applicati a oggetti mutabili, quelli sono:

  1. Se a.Equals(b) Poi a.GetHashCode() Dovrebbe == b.GetHashCode().
  2. Il valore di a.GetHashCode() non dovrebbe mai cambiare per tutta la vita a.

Questi sembrano naturalmente contraddittori quando si tratta di un oggetto mutabile, perché se lo stato dell'oggetto cambia, ci aspettiamo il valore di .Equals() cambiare, il che significa questo GetHashCode dovrebbe cambiare per adattarsi al cambiamento .Equals(), Ma GetHashCode non dovrebbe cambiare.

Perché sembra esserci questa contraddizione?Queste raccomandazioni non sono pensate per essere applicate agli oggetti mutabili?Probabilmente presupposto, ma potrebbe valere la pena ricordare che mi riferisco a classi e non a strutture.

Risoluzione:

Sto contrassegnando JaredPar come accettato, ma principalmente per l'interazione dei commenti.Per riassumere ciò che ho imparato da questo è che l'unico modo per raggiungere tutti gli obiettivi ed evitare possibili comportamenti bizzarri nei casi limite è semplicemente eseguire l'override Equals E GetHashCode basato su campi immutabili o implementare IEquatable.Questo tipo di sembra diminuire l'utilità dell'override Equals per i tipi di riferimento, da quello che ho visto la maggior parte dei tipi di riferimento di solito non hanno campi immutabili a meno che non siano archiviati in un database relazionale per identificarli con le loro chiavi primarie.

È stato utile?

Soluzione

come funziona, se i campi che si basa su sono mutabili?

Non è così, nel senso che il codice hash cambierà come i cambiamenti degli oggetti. Questo è un problema per tutti i motivi elencati negli articoli che si leggono. Purtroppo questo è il tipo di problema che di solito mostrano solo in casi d'angolo. Così gli sviluppatori tendono a farla franca con il cattivo comportamento.

Anche se io voglio le ricerche del dizionario ecc per essere basato sulla parità di riferimento non miei pari ignorati?

Fino a quando si implementa un'interfaccia simile IEquatable<T> questo non dovrebbe essere un problema. La maggior parte delle implementazioni del dizionario sceglieranno un confronto di uguaglianza in un modo che utilizzerà IEqualityComparer<T> sopra Object.ReferenceEquals. Anche senza EqualityComparer<T>.Default, la maggior parte di default a chiamare Object.Equals () che sarà poi entra nella tua implementazione.

In sostanza nella maggior parte del codice in esecuzione voglio uguaglianza di riferimento e io uso sempre == e non sto ignorando che.

Se vi aspettate che i vostri oggetti di comportarsi con l'uguaglianza valore che si dovrebbe ignorare == e! = Per far rispettare l'uguaglianza di valore per tutti i confronti. Gli utenti possono comunque utilizzare Object.ReferenceEquals se effettivamente vogliono l'uguaglianza di riferimento.

Ho usato per pensare che il quadro utilizza sempre == e non equivale a confrontare le cose

Quali gli usi BCL è cambiato un po 'nel corso del tempo. Ora la maggior parte dei casi che utilizzano l'uguaglianza avranno un <=> istanza e utilizzarlo per l'uguaglianza. Nei casi in cui uno non è specificato che useranno <=> di trovare uno. In questo caso peggiore sarà di default a chiamare Object.Equals

Altri suggerimenti

Se si dispone di un oggetto mutabile, non c'è molto senso override del metodo GetHashCode, come non si può davvero usare. E 'utilizzato ad esempio dalla Dictionary e HashSet collezioni di collocare ogni elemento in un secchio. Se si modifica l'oggetto quando viene utilizzato come chiave della collezione, il codice hash non corrisponde più il secchio che l'oggetto è in, così la raccolta non funziona correttamente e non può mai ritrovare l'oggetto.

Se si desidera che la ricerca di non utilizzare il metodo GetHashCode o Equals della classe, si può sempre fornire la propria implementazione IEqualityComparer da usare al posto quando si crea il <=>.

Il metodo <=> è destinato per l'uguaglianza di valore, quindi non è male per la sua attuazione in questo modo.

Wow, in realtà ci sono più domande in una :-).Quindi uno dopo l'altro:

è stato affermato che il valore di GetHashCode non dovrebbe mai cambiare nel corso della vita dell'oggetto.Come funziona se i campi su cui si basa sono mutabili?

Questo consiglio comune è pensato per il caso in cui desideri utilizzare il tuo oggetto come chiave in una HashTable/dizionario ecc..Le HashTable di solito richiedono che l'hash non cambi, perché lo usano per decidere come archiviare e recuperare la chiave.Se l'hash cambia, HashTable probabilmente non troverà più il tuo oggetto.

Per citare i documenti di Java Carta geografica interfaccia:

Nota:è necessario prestare molta attenzione se si utilizzano oggetti mutabili come chiavi della mappa.Il comportamento di una mappa non è specificato se il valore di un oggetto viene modificato in modo da influenzare i confronti uguali mentre l'oggetto è una chiave nella mappa.

In generale è una cattiva idea da usare Qualunque tipo di oggetto mutabile come chiave in una tabella hash:Non è nemmeno chiaro cosa dovrebbe accadere se una chiave cambia dopo essere stata aggiunta alla tabella hash.La tabella hash dovrebbe restituire l'oggetto memorizzato tramite la vecchia chiave, o tramite la nuova chiave, o tramite entrambi?

Quindi il vero consiglio è:Utilizza solo oggetti immutabili come chiavi e assicurati che anche il loro hashcode non cambi mai (il che di solito è automatico se l'oggetto è immutabile).

Inoltre, cosa succede se voglio che le ricerche nel dizionario, ecc., siano basate sull'uguaglianza dei riferimenti e non sui miei Equals sovrascritti?

Bene, trova un'implementazione del dizionario che funzioni in questo modo.Ma i dizionari della libreria standard utilizzano hashcode&Equals e non c'è modo di cambiarlo.

Principalmente sto sovrascrivendo Equals per la facilità di test unitario del mio codice di serializzazione che presumo che la serializzazione e la deserializzazione (in XML nel mio caso) uccidano l'uguaglianza di riferimento, quindi voglio assicurarmi che almeno sia corretta per uguaglianza di valori.È una cattiva pratica sovrascrivere Equals in questo caso?

No, lo troverei perfettamente accettabile.Tuttavia, non dovresti utilizzare oggetti come chiavi in ​​un dizionario/tabella hash, poiché sono modificabili.Vedi sopra.

Il tema di fondo qui è come al meglio identificare in modo univoco gli oggetti. Lei parla di serializzazione / deserializzazione che è importante perché l'integrità referenziale si perde in quel processo.

La risposta breve, è che gli oggetti devono essere identificati in modo univoco dal più piccolo insieme di campi immutabili che possono essere utilizzati per farlo. Questi sono i campi che si dovrebbe usare quando overrideing GetHashCode ed è uguale.

Per la prova è perfettamente ragionevole per definire tutto ciò affermazioni è necessario, di solito queste non vengono definite dal tipo in sé, ma piuttosto come metodi di utilità nella suite di test. Forse un TestSuite.AssertEquals (MyClass, MyClass)?

Si noti che GetHashCode e Equals dovrebbero lavorare insieme. GetHashCode deve restituire lo stesso valore per due oggetti se sono uguali. Eguali devono restituire true se e solo se due oggetti hanno lo stesso codice hash. (Si noti che è possibile che due oggetti non può essere uguale ma può restituire lo stesso codice hash). Ci sono un sacco di pagina web che affrontare questo argomento a testa alta, appena google via.

Non so su C #, essendo un noob rispetto ad esso, ma in Java, se si ignora equals () è necessario sostituire anche hashCode () per mantenere il contratto tra loro (e viceversa) .. . e Java ha anche lo stesso cattura 22; fondamentalmente forzando si utilizza campi immutabili ... ma questo è un problema solo per le classi che vengono utilizzate come un-tasto cancelletto, e Java è implementazioni alternative per tutte le collezioni hash-based ... che forse non così rapidamente, ma lo fanno effecitely consentono di utilizzare un oggetto mutabile come una chiave ... è solo (di solito) accigliò come una "cattiva progettazione".

E mi sento il bisogno di sottolineare che questo problema fondamentale è senza tempo ... E 'stato intorno dal Adam era un ragazzo.

Ho lavorato sul codice FORTRAN che è più vecchio di me (io sono 36) che si rompe quando un nome utente viene modificato (come quando una ragazza si sposa, o divorziato ;-) ... Così è l'ingegneria, la soluzione adottata è stata: il "metodo" GetHashCode ricorda il hashCode precedentemente calcolato, ricalcola il hashCode (vale a dire un indicatore IsDirty virtuale) e se i keyfields sono cambiate restituisce null. Questo fa sì che la cache per eliminare l'utente "sporco" (chiamando un altro GetPreviousHashCode) e poi la cache restituisce null, causando l'utente a rileggere dal database. Un trucco interessante e utile; anche se io lo dico io; -)

Ti trade-off mutevolezza (solo auspicabile in casi d'angolo) per O (1) accesso (auspicabile in tutti i casi). Benvenuto in ingegneria; la terra del compromesso informato.

Saluti. Keith.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top