Cela fait longtemps, mais je pense néanmoins qu'il est encore nécessaire de donner une réponse correcte à cette question, y compris des explications sur le pourquoi et le comment. La meilleure réponse à ce jour est celle citant le MSDN de manière exhaustive - n'essayez pas de faire vos propres règles, les gars de MS savaient ce qu'ils faisaient.
Mais tout d'abord: la ligne directrice citée dans la question est fausse.
Maintenant les pourquoi - il y en a deux
Premièrement pourquoi : si le hashcode est calculé d'une manière, qu'il ne change pas pendant la durée de vie d'un objet, même si l'objet lui-même change, alors il romprait le contrat égal.
N'oubliez pas: «Si deux objets se comparent comme égaux, la méthode GetHashCode de chaque objet doit renvoyer la même valeur. Cependant, si deux objets ne sont pas comparables, les méthodes GetHashCode pour les deux objets ne doivent pas renvoyer des valeurs différentes.»
La deuxième phrase est souvent mal interprétée comme "La seule règle est qu'au moment de la création de l'objet, le hashcode des objets égaux doit être égal". Je ne sais pas vraiment pourquoi, mais c'est là aussi l'essence de la plupart des réponses.
Pensez à deux objets contenant un nom, où le nom est utilisé dans la méthode equals: Même nom -> même chose. Créer une instance A: Nom = Joe Créer une instance B: Nom = Peter
Hashcode A et Hashcode B ne seront probablement pas les mêmes. Que se passerait-il maintenant, lorsque le nom de l'instance B serait changé en Joe?
Selon la directive de la question, le hashcode de B ne changerait pas. Le résultat serait: A.Equals (B) ==> true Mais en même temps: A.GetHashCode () == B.GetHashCode () ==> false.
Mais exactement ce comportement est explicitement interdit par le contrat equals & hashcode.
Deuxièmement, pourquoi : bien qu'il soit - bien sûr - vrai, que des changements dans le hashcode pourraient casser les listes hachées et d'autres objets utilisant le hashcode, l'inverse est également vrai. Ne pas modifier le hashcode, dans le pire des cas, obtiendra des listes hachées, où tous les objets différents auront le même hashcode et seront donc dans le même hachage - cela se produit lorsque les objets sont initialisés avec une valeur standard, par exemple.
Voyons maintenant comment Eh bien, à première vue, il semble y avoir une contradiction - de toute façon, le code se cassera. Mais aucun des problèmes ne vient du hashcode changé ou inchangé.
La source des problèmes est bien décrite dans le MSDN:
À partir de l'entrée de hachage de MSDN:
Les objets clés doivent être immuables tant qu'ils sont utilisés comme clés dans la table de hachage.
Cela veut dire:
Tout objet qui crée une valeur de hachage doit changer la valeur de hachage, lorsque l'objet change, mais il ne doit pas - ne doit absolument pas - autoriser aucune modification de lui-même, lorsqu'il est utilisé dans une table de hachage (ou tout autre objet utilisant Hash, bien sûr) .
Tout d'abord, la manière la plus simple serait bien sûr de concevoir des objets immuables uniquement pour une utilisation dans des tables de hachage, qui seront créés comme des copies des objets normaux, les objets mutables en cas de besoin. À l'intérieur des objets immuables, il est évidemment correct de mettre en cache le hashcode, car il est immuable.
Deuxièmement, ou donnez à l'objet un drapeau "vous êtes haché maintenant", assurez-vous que toutes les données de l'objet sont privées, vérifiez le drapeau dans toutes les fonctions qui peuvent changer les données des objets et lancez une donnée d'exception si le changement n'est pas autorisé (c'est-à-dire que le drapeau est défini ). Maintenant, lorsque vous placez l'objet dans une zone hachée, assurez-vous de définir l'indicateur et, également, de désactiver l'indicateur, lorsqu'il n'est plus nécessaire. Pour faciliter l'utilisation, je vous conseille de définir automatiquement le drapeau dans la méthode "GetHashCode" - de cette façon, il ne peut pas être oublié. Et l'appel explicite d'une méthode "ResetHashFlag" garantira que le programmeur devra réfléchir, qu'il soit ou non autorisé à modifier les données des objets pour le moment.
Ok, que faut-il dire aussi: il y a des cas, où il est possible d'avoir des objets avec des données mutables, où le hashcode est néanmoins inchangé, lorsque les données des objets sont modifiées, sans violer le contrat equals & hashcode-contract.
Cela nécessite cependant que la méthode equals ne soit pas basée non plus sur les données mutables. Donc, si j'écris un objet et que je crée une méthode GetHashCode qui ne calcule une valeur qu'une seule fois et la stocke dans l'objet pour le renvoyer lors d'appels ultérieurs, alors je dois, encore une fois: absolument obligé, créer une méthode Equals, qui utilisera valeurs stockées pour la comparaison, de sorte que A.Equals (B) ne passera jamais non plus de faux à vrai. Sinon, le contrat serait rompu. Le résultat de cela sera généralement que la méthode Equals n'a aucun sens - ce n'est pas la référence d'origine est égale, mais ce n'est pas non plus une valeur égale. Parfois, cela peut être un comportement intentionnel (c.-à-d. Les enregistrements des clients), mais ce n'est généralement pas le cas.
Donc, modifiez simplement le résultat de GetHashCode, lorsque les données de l'objet changent, et si l'utilisation de l'objet à l'intérieur du hachage à l'aide de listes ou d'objets est prévue (ou simplement possible), rendez l'objet soit immuable ou créez un indicateur en lecture seule à utiliser pour le durée de vie d'une liste hachée contenant l'objet.
(À propos: tout cela n'est pas spécifique à C # ou .NET - c'est dans la nature de toutes les implémentations de table de hachage, ou plus généralement de toute liste indexée, que les données d'identification des objets ne doivent jamais changer, tant que l'objet est dans la liste . Un comportement inattendu et imprévisible se produira si cette règle est enfreinte. Quelque part, il peut y avoir des implémentations de liste, qui surveillent tous les éléments de la liste et effectuent une réindexation automatique de la liste - mais les performances de celles-ci seront sûrement horribles au mieux.)