Qual è la migliore strategia per Equals e GetHashCode?

Sto lavorando con un modello di dominio e stavo pensando ai vari modi in cui dobbiamo implementare questi due metodi in .NET. Qual è la tua strategia preferita?

Questa è la mia attuale implementazione:

public override bool Equals(object obj) { var newObj = obj as MyClass; if (null != newObj) { return this.GetHashCode() == newObj.GetHashCode(); } else { return base.Equals(obj); } } //Since this is an entity I can use it´s Id //When I don´t have an Id I usually make a composite key of the properties public override int GetHashCode() { return String.Format("MyClass{0}", this.Id.ToString()).GetHashCode(); } 

Supponendo che le istanze siano uguali perché i codici hash sono uguali è sbagliato.

Immagino che la tua implementazione di GetHashCode sia OK, ma di solito uso cose simili a questa:

 public override int GetHashCode() { return object1.GetHashCode ^ intValue1 ^ (intValue2 << 16); } 

Domain-Driven Design fa la distinzione tra entity framework e oggetti valore . Questa è una buona distinzione da osservare poiché guida l’implementazione di Equals.

Le quadro sono uguali se i loro ID sono uguali tra loro.

Gli oggetti valore sono uguali se tutti i loro elementi (importanti) costitutivi sono uguali tra loro.

In ogni caso, l’implementazione di GetHashCode dovrebbe basarsi sugli stessi valori utilizzati per determinare l’uguaglianza. In altre parole, per le Entità, il codice hash dovrebbe essere calcolato direttamente dall’ID, mentre per gli Oggetti valore dovrebbe essere calcolato da tutti i valori costitutivi.

Nessuna delle risposte qui è davvero azzeccata per me. Dato che hai già detto che non puoi usare Id per l’uguaglianza e devi usare un pacchetto di proprietà, ecco un modo migliore per farlo. Nota: non ritengo che questo sia il modo migliore per implementare Equals e GetHashCode . Questa è una versione migliore del codice dell’OP.

 public override bool Equals(object obj) { var myClass = obj as MyClass; if (null != myClass) { // Order these by the most different first. // That is, whatever value is most selective, and the fewest // instances have the same value, put that first. return this.Id == myClass.Id && this.Name == myClass.Name && this.Quantity == myClass.Quantity && this.Color == myClass.Color; } else { // Not sure this makes sense! return base.Equals(obj); } } public override int GetHashCode() { int hash = 19; unchecked { // allow "wrap around" in the int hash = hash * 31 + this.Id; // assuming integer hash = hash * 31 + this.Name.GetHashCode(); hash = hash * 31 + this.Quantity; // again assuming integer hash = hash * 31 + this.Color.GetHashCode(); } return hash; } 

Vedi questa risposta di Jon Skeet per alcuni dei ragionamenti alla base di questo. L’uso di xor non va bene perché vari set di dati possono finire con lo stesso hash. Questo metodo wrap-around con i numeri primi (i valori seme di 19 e 31 sopra, o altri valori che si scelgono) fa un lavoro migliore di segmentazione in “bucket” che hanno solo collisioni ciascuno.

Se uno qualsiasi dei tuoi valori può essere nullo, ti incoraggio a riflettere attentamente su come dovrebbero essere confrontati. Potresti usare la valutazione nulla di cortocircuito e forse l’operatore di coalescenza nulla. Ma assicurati che se i valori nulli debbano essere confrontati come uguali, assegni codici hash diversi alle diverse proprietà nullable quando sono nulli.

Inoltre, non sono convinto che l’implementazione di Equals abbia senso. Quando due oggetti vengono confrontati per l’uguaglianza, prima vengono confrontati i loro valori GetHashCode . Solo se questi sono diversi viene eseguito il metodo Equals (in modo che se due oggetti che hanno hash sullo stesso valore siano diversi, questo verrà rilevato). Poiché l’implementazione GetHashCode non si riferisce alla base , non ha senso che il metodo Equals faccia ciò.

I codici hash possono scontrarsi quindi non penso che siano un buon modo per confrontare l’uguaglianza. Dovresti confrontare i valori sottostanti che rendono gli oggetti “uguali”. Vedi @Jon Skeet’s risposta a questa domanda: Qual è il miglior algoritmo per un System.Object.GetHashCode sovrascritto? per una migliore implementazione GetHashCode se la tua uguaglianza comprende diverse proprietà. Se è solo una singola proprietà, puoi semplicemente riutilizzarla come hashcode.

Mi sono imbattuto in questa vecchia domanda e, IMHO, non ho trovato alcuna risposta chiara e semplice ha dichiarato la domanda originale formulata da @tucaz.

Posso essere d’accordo con molte considerazioni condivise sopra (o sotto: D) ma il «Punto interrogativo» è mancato (credo).

Purché:

  • L’uguaglianza è richiesta per le quadro
  • Gli oggetti- quadro possono essere considerati uguali se mappano la stessa entity framework, mentre si riferiscono alla stessa «chiave dell’ quadro»
  • L’esempio mostrato da @tucaz menziona solo un «Id» (vedi il GetHashCode () troppo implementato) … per non parlare del buggy Equals (…)

Posso immaginare che una semplice implementazione potrebbe essere:

 public class MyEntity: IEquatable { int Id; public MyEntity(int id){ Id = id; } public override bool Equals(object obj) => Equals(obj as MyEntity); public bool Equals(MyEntity obj) => obj != null && Id == obj.Id; public override int GetHashCode() => Id; } 

È tutto!