Comment l'implémentation par défaut pour GetHashCode()
? Et gère-t-il suffisamment et efficacement les structures, les classes, les tableaux, etc.?
J'essaie de décider dans quels cas je dois emballer le mien et dans quels cas je peux compter en toute sécurité sur l'implémentation par défaut pour bien faire. Je ne veux pas réinventer la roue, si possible.
.net
hash
gethashcode
Fung
la source
la source
GetHashCode()
a été remplacé) en utilisantSystem.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj)
Réponses:
InternalGetHashCode est mappé à une fonction ObjectNative :: GetHashCode dans le CLR, qui ressemble à ceci:
L'implémentation complète de GetHashCodeEx est assez volumineuse, il est donc plus facile de simplement créer un lien vers le code source C ++ .
la source
string
substitutionsGetHashCode
. D'autre part, supposons que vous souhaitiez conserver un décompte du nombre de fois que les différents contrôles traitent lesPaint
événements. Vous pouvez utiliser unDictionary<Object, int[]>
(chaqueint[]
stocké contiendrait exactement un élément).Pour une classe, les valeurs par défaut sont essentiellement l'égalité de référence, et c'est généralement bien. Si vous écrivez une structure, il est plus courant de surcharger l'égalité (notamment pour éviter la boxe), mais il est très rare d'écrire une structure de toute façon!
Lorsque vous remplacez l'égalité, vous devriez toujours avoir une correspondance
Equals()
etGetHashCode()
(c'est-à-dire pour deux valeurs, siEquals()
retourne true, elles doivent retourner le même code de hachage, mais l'inverse n'est pas obligatoire) - et il est courant de fournir également des opérateurs==
/!=
, et souvent de mettre en œuvreIEquatable<T>
aussi.Pour générer le code de hachage, il est courant d'utiliser une somme pondérée, car cela évite les collisions sur des valeurs appariées - par exemple, pour un hachage de base à 2 champs:
Cela présente l'avantage que:
etc - ce qui peut être courant si vous utilisez simplement une somme non pondérée, ou xor (
^
), etc.la source
unchecked
. Heureusement,unchecked
c'est la valeur par défaut en C #, mais il serait préférable de la rendre explicite; éditéLa documentation de la
GetHashCode
méthode pour Object indique que "l'implémentation par défaut de cette méthode ne doit pas être utilisée comme identificateur d'objet unique à des fins de hachage". et celui de ValueType indique "Si vous appelez la méthode GetHashCode du type dérivé, la valeur de retour n'est probablement pas adaptée à une utilisation comme clé dans une table de hachage.".Les types de données de base comme
byte
,short
,int
,long
,char
etstring
mettre en œuvre une bonne méthode GetHashCode. Certaines autres classes et structures, commePoint
par exemple, implémentent unGetHashCode
méthode qui peut ou non convenir à vos besoins spécifiques. Il vous suffit de l'essayer pour voir si c'est assez bon.La documentation de chaque classe ou structure peut vous dire si elle remplace ou non l'implémentation par défaut. S'il ne le remplace pas, vous devez utiliser votre propre implémentation. Pour toutes les classes ou structures que vous créez vous-même où vous devez utiliser la
GetHashCode
méthode, vous devez créer votre propre implémentation qui utilise les membres appropriés pour calculer le code de hachage.la source
Comme je n'ai pas trouvé de réponse expliquant pourquoi nous devrions remplacer
GetHashCode
etEquals
pour les structures personnalisées et pourquoi l'implémentation par défaut "n'est pas susceptible d'être utilisée comme clé dans une table de hachage", je vais laisser un lien vers ce blog post , ce qui explique pourquoi avec un exemple concret d'un problème survenu.Je recommande de lire l'intégralité du message, mais voici un résumé (soulignement et clarifications ajoutés).
Raison pour laquelle le hachage par défaut pour les structures est lent et pas très bon:
Problème du monde réel décrit dans l'article:
Donc, pour répondre à la question "dans quels cas je devrais emballer le mien et dans quels cas je peux compter en toute sécurité sur l'implémentation par défaut", au moins dans le cas des structures , vous devez surcharger
Equals
etGetHashCode
chaque fois que votre structure personnalisée peut être utilisée comme un entrez une table de hachage ouDictionary
.Je recommanderais également la mise
IEquatable<T>
en œuvre dans ce cas, pour éviter la boxe.Comme le disent les autres réponses, si vous écrivez une classe , le hachage par défaut utilisant l'égalité de référence est généralement correct, donc je ne me dérangerais pas dans ce cas, à moins que vous n'ayez besoin de remplacer
Equals
(alors vous devrez remplacer enGetHashCode
conséquence).la source
De manière générale, si vous remplacez Equals, vous souhaitez remplacer GetHashCode. La raison en est que les deux sont utilisés pour comparer l'égalité de votre classe / structure.
Equals est utilisé lors de la vérification de Foo A, B;
si (A == B)
Puisque nous savons que le pointeur n'est pas susceptible de correspondre, nous pouvons comparer les membres internes.
GetHashCode est généralement utilisé par les tables de hachage. Le hashcode généré par votre classe doit toujours être le même pour une classe donnant l'état.
Je fais généralement,
Certains diront que le hashcode ne devrait être calculé qu'une fois par durée de vie d'objet, mais je ne suis pas d'accord avec cela (et je me trompe probablement).
En utilisant l'implémentation par défaut fournie par object, à moins que vous n'ayez la même référence à l'une de vos classes, elles ne seront pas égales l'une à l'autre. En remplaçant Equals et GetHashCode, vous pouvez signaler l'égalité basée sur des valeurs internes plutôt que sur la référence des objets.
la source
Si vous avez uniquement affaire à des POCO, vous pouvez utiliser cet utilitaire pour vous simplifier quelque peu la vie:
...
la source