57 votes

La substitution de GetHashCode pour les objets mutables ? [C#]

J'ai lu sur 10 questions sur quand et comment remplacer GetHashCode mais il y a encore quelque chose que je n'ai pas tout à fait le faire. La plupart des implémentations de GetHashCode sont fondées sur les codes de hachage des champs de l'objet, mais il a été cité que la valeur de GetHashCode ne faut pas changer au cours de la durée de vie de l'objet. Comment cela fonctionne si les champs que c'est basé sur sont mutables? Aussi que faire si je ne veux dictionnaire des recherches etc pour être fondé sur la référence à l'égalité a pas remplacé mon Égal?

Je suis principalement primordial est Égal pour la facilité de l'unité de tester mon code de sérialisation qui, je suppose la sérialisation et la désérialisation (XML dans mon cas) tue la référence à l'égalité, donc je veux m'assurer au moins qu'il est approprié par la valeur de l'égalité. Est-ce une mauvaise pratique de remplacer Équivaut dans ce cas? Fondamentalement, en plus de l'exécution de code que j'ai envie de référence de l'égalité et j'ai toujours utiliser == et je ne suis pas primordial que. Devrais-je créer une nouvelle méthode ValueEquals ou quelque chose à la place primordiale à l'Égal? J'ai utilisé de supposer que le cadre utilise toujours == et non Équivaut à comparer des choses et donc je pensais qu'il était sûr à remplacer est égal, car il me semblait comme le but est pour, si vous voulez avoir un 2ème définition de l'égalité qui est différent de l'opérateur==. À la lecture de plusieurs autres questions mais il semble que ce n'est pas le cas.

EDIT:

Il semble que mes intentions n'étaient pas claires, ce que je veux dire, c'est que 99% du temps, je veux ancienne brut de référence de l'égalité, le comportement par défaut, pas de surprises. Pour des cas très rares, je veux avoir la valeur d'égalité, et je tiens à la demande explicite de la valeur de l'égalité à l'aide de ".Équivaut à" au lieu de "==".

Quand je fais cela, le compilateur recommande je remplacer GetHashCode ainsi, et c'est la façon dont cette question est venu. Il me semblait qu'il y est de contredire les objectifs pour GetHashCode lorsqu'il est appliqué à mutable objets, qui sont les suivants:

  1. Si un.Equals(b) puis une.GetHashCode() doit == b.GetHashCode()
  2. La valeur de un.GetHashCode() ne devraient jamais changer pour la durée de vie d'un

Ceux-ci semblent naturellement contredire lorsqu'un objet mutable, parce que si l'état de l'objet de modifications, nous nous attendons à ce que la valeur de .Equals() pour modifier, ce qui signifie que GetHashCode devrait changer pour correspondre le changement .Equals(), mais GetHashCode ne devrait pas changer.

Pourquoi ne semble y avoir cette contradiction? Ces recommandations sont-elles pas vocation à s'appliquer à des objets mutables? Probablement supposé, mais peut-être la peine de mentionner, je fais allusion à des classes non des structures.

Résolution:

Je suis marquage JaredPar acceptée, mais surtout pour les commentaires de l'interaction. Pour résumer ce que j'ai appris c'est que la seule façon d'atteindre tous les objectifs et d'éviter d'éventuelles bizarre comportement en cas de bord est à remplacer que d'égal à Égal et GetHashCode basé sur immuable champs, ou de mettre en œuvre IEquatable. Ce genre de semble diminuer l'utilité de remplacement est Égal pour les types référence, à partir de ce que j'ai vu la plupart des types de référence n'ont généralement pas immuable champs, sauf si elles sont stockées dans une base de données relationnelle de les identifier avec leurs clés primaires.

22voto

JaredPar Points 333733

Comment cela fonctionne si les champs que c'est basé sur sont mutables?

Il n'est pas dans le sens que le code de hachage va changer à mesure que l'objet de modifications. C'est un problème pour toutes les raisons énumérées dans les articles que vous lisez. Malheureusement, c'est le type de problème qui généralement ne se montre que dans le cas du coin. Afin que les développeurs ont tendance à sortir avec le mauvais comportement.

Aussi que faire si je ne veux dictionnaire des recherches etc pour être fondé sur la référence à l'égalité a pas remplacé mon Égal?

Aussi longtemps que vous le mettre en œuvre une interface comme IEquatable<T> cela ne devrait pas être un problème. La plupart des dictionnaire les implémentations de choisir un comparateur d'égalité de manière à utiliser IEquatable<T> sur l'Objet.ReferenceEquals. Même sans IEquatable<T>, plus par défaut à l'Objet appelant.Equals() qui va ensuite aller dans votre mise en œuvre.

Fondamentalement, en plus de l'exécution de code que j'ai envie de référence de l'égalité et j'ai toujours utiliser == et je ne suis pas primordial que.

Si vous attendez de vos objets à se comporter avec la valeur de l'égalité, vous devez remplacer == et != pour appliquer la valeur de l'égalité pour toutes les comparaisons. Les utilisateurs peuvent toujours utiliser l'Objet.ReferenceEquals si ils veulent réellement référence à l'égalité.

J'ai utilisé de supposer que le cadre utilise toujours == et non Équivaut à comparer des choses

Ce que la BCL utilise a un peu changé au fil du temps. Maintenant la plupart des cas qui utilisent l'égalité va prendre un IEqualityComparer<T> de l'instance et de l'utiliser pour l'égalité. Dans le cas où l'on n'est pas spécifié qu'ils utiliseront EqualityComparer<T>.Default à en trouver un. Au pire des cas ce sera par défaut à l'Objet appelant.Est égal à

6voto

Guffa Points 308133

Si vous avez un objet mutable, il n'y a pas beaucoup de point en surchargeant la méthode GetHashCode, que vous ne pouvez pas vraiment l'utiliser. Il est utilisé par exemple par l' Dictionary et HashSet collections de placer chaque élément dans un seau. Si vous modifiez l'objet lorsqu'il est utilisé comme un élément clé dans la collecte, le code de hachage ne correspond plus à le seau que l'objet est, de sorte que la collection ne fonctionne pas correctement et vous ne pouvez jamais trouver de nouveau l'objet.

Si vous voulez la recherche de ne pas utiliser l' GetHashCode ou Equals méthode de la classe, vous pouvez toujours fournir votre propre IEqualityComparer mise en œuvre pour utiliser au lieu de cela lorsque vous créez l' Dictionary.

L' Equals méthode est conçue pour une valeur d'égalité, il n'est donc pas de mal à l'appliquer de cette façon.

4voto

sleske Points 29978

Wow, c'est en fait plusieurs questions en une :-). Ainsi, l'une après l'autre:

il a été cité que la valeur de GetHashCode ne faut pas changer au cours de la durée de vie de l'objet. Comment cela fonctionne si les champs que c'est basé sur sont mutables?

Cette commune de conseils est destinée pour le cas où vous souhaitez utiliser votre objet comme une clé dans une table de hachage/dictionnaire etc. . Les tables de hashage nécessitent généralement la valeur de hachage pour ne pas changer, parce qu'ils utilisent pour décider de la façon de magasin et récupérer la clé. Si les modifications de hachage, la table de hachage sera probablement de ne plus retrouver votre objet.

Pour citer la documentation de Java est la Carte d'interface:

Remarque: le plus grand soin doit être exercé si mutable objets sont utilisés comme une carte de clés. Le comportement d'une carte n'est pas spécifié si la valeur d'un objet est modifié d'une manière qui affecte égale comparaisons tandis que l'objet est un élément clé dans la carte.

En général c'est une mauvaise idée d'utiliser tout type de mutable objet comme une clé dans une table de hachage: Il n'est même pas clair ce qui se passerait si une clé changements après qu'il a été ajouté à la table de hachage. Si la table de hachage de retour de l'objet stocké via l'ancienne clé, ou par l'intermédiaire de la nouvelle clé, ou par les deux?

Donc le réel des conseils: utilisez Uniquement des objets immuables comme les clés, et assurez-vous de leur hashcode ne change jamais, soit (ce qui est généralement automatique si l'objet est immuable).

Aussi que faire si je ne veux dictionnaire des recherches etc pour être fondé sur la référence à l'égalité a pas remplacé mon Égal?

Eh bien, trouver un dictionnaire de mise en œuvre qui fonctionne comme ça. Mais la bibliothèque standard de l'utilisation des dictionnaires le hashcode&d'égal à Égal, et il n'y a aucun moyen de changer cela.

Je suis principalement primordial est Égal pour la facilité de l'unité de tester mon code de sérialisation qui, je suppose la sérialisation et la désérialisation (XML dans mon cas) tue la référence à l'égalité, donc je veux m'assurer au moins qu'il est approprié par la valeur de l'égalité. Est-ce une mauvaise pratique de remplacer Équivaut dans ce cas?

Non, je trouverais parfaitement acceptable. Cependant, vous ne devez pas utiliser ces objets comme des clés dans un dictionnaire/table de hachage, comme ils sont mutables. Voir ci-dessus.

2voto

corlettk Points 5051

Je ne sais pas à propos de C#, étant un noob par rapport à elle, mais en Java, si vous substituez equals (), vous devez également remplacer hashCode() pour maintenir le contrat entre eux (et vice-versa)... Et le java a aussi le même catch 22; fondamentalement, vous forçant l'utilisation immuable champs... Mais c'est un problème que pour les classes qui sont utilisées en tant que mot-clé, et Java a d'autres implémentations pour tous à base de hachage collections... qui est peut-être pas aussi vite, mais ils ne effecitely vous permettent d'utiliser un objet mutable comme l'un des principaux... c'est juste (en général) fronça les sourcils comme une "mauvaise conception".

Et j'ai envie de souligner que ce problème fondamental est intemporelle... Il a été autour depuis Adam était un garçon.

J'ai travaillé sur le code fortran qui est plus âgé que moi (je suis à 36), qui se brise lorsqu'un nom d'utilisateur est modifié (par exemple, quand une fille se marie, ou divorcée ;-) ... ce qui est de l'ingénierie, La solution retenue a été: La GetHashCode "méthode", se souvient l'calculée précédemment hashCode, recalcule la hashCode (c'est à dire un virtuel isDirty marqueur) et si le keyfields ont changé, elle renvoie null. Cela provoque le cache pour supprimer le "sale" de l'utilisateur (par l'appel d'une autre GetPreviousHashCode) et puis le cache renvoie la valeur null, obligeant l'utilisateur à re-lire à partir de la base de données. Un intéressant hack; même si je ne le dire moi-même ;-)

Je vais compromis mutabilité (seulement souhaitable, en cas de coin) pour O(1) accès (souhaitable dans tous les cas). Bienvenue à l'ingénierie; la terre de la connaissance de compromis.

Des acclamations. Keith.

1voto

Joe Points 80

Le sujet traité ici est de savoir comment mieux identifier les objets. Vous mentionnez la sérialisation/désérialisation qui est important parce que l'intégrité référentielle est perdu dans le processus.

La réponse courte, c'Est que les objets doivent être identifiés de façon unique par le plus petit ensemble de immuable champs qui peuvent être utilisés pour ce faire. Ce sont les champs que vous devez utiliser lorsque overrideing GetHashCode et d'égal à Égal.

Pour les tests il est parfaitement raisonnable de définir quelles que soient les affirmations vous avez besoin, généralement ceux-ci ne sont pas définies sur le type lui-même, mais plutôt comme des méthodes utiles dans la suite de test. Peut-être une suite de tests.AssertEquals(MyClass, MyClass) ?

Notez que GetHashCode et est Égal à travailler ensemble. GetHashCode doit retourner la même valeur pour les deux objets s'ils sont égaux. Est égal doit retourner true si et seulement si deux objets ont le même code de hachage. (Notez qu'il est possible que les deux objets ne peuvent pas être égaux, mais peut renvoyer le même code de hachage). Il y a beaucoup de page web qui abordent ce sujet de front, il suffit de google à l'écart.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X