web-dev-qa-db-fra.com

Qu'est-ce qui doit être ignoré dans une structure pour garantir le bon fonctionnement de l'égalité?

Comme le titre l'indique: ai-je besoin de remplacer l'opérateur ==? que diriez-vous de la méthode .Equals()? Tout ce qui me manque?

70
RCIX

Un exemple de msdn

public struct Complex 
{
   double re, im;
   public override bool Equals(Object obj) 
   {
      return obj is Complex && this == (Complex)obj;
   }
   public override int GetHashCode() 
   {
      return re.GetHashCode() ^ im.GetHashCode();
   }
   public static bool operator ==(Complex x, Complex y) 
   {
      return x.re == y.re && x.im == y.im;
   }
   public static bool operator !=(Complex x, Complex y) 
   {
      return !(x == y);
   }
}
83
UpTheCreek

Vous devez également implémenter IEquatable <T>. Voici un extrait des directives de conception du cadre:

IMPLÉMENTER IEquatable sur les types de valeur. La méthode Object.Equals sur les types de valeurs provoque la boxe et son implémentation par défaut n'est pas très efficace car elle utilise la refection. IEquatable.Equals peut offrir de bien meilleures performances et peut être implémenté de sorte qu'il ne cause pas de boxe.

public struct Int32 : IEquatable<Int32> {
    public bool Equals(Int32 other){ ... }
}

SUIVEZ les mêmes instructions que pour remplacer Object.Equals lors de l'implémentation d'IEquatable.Equals. Voir la section 8.7.1 pour des instructions détaillées sur la substitution d'Object.

44
Dzmitry Huba

Malheureusement, je n'ai pas assez de réputation pour commenter d'autres entrées. Je publie donc une amélioration possible de la meilleure solution ici.

Corrigez-moi, si je me trompe, mais la mise en œuvre mentionnée ci-dessus

public struct Complex 
{
   double re, im;
   public override bool Equals(Object obj) 
   {
      return obj is Complex && this == (Complex)obj;
   }
   public override int GetHashCode() 
   {
      return re.GetHashCode() ^ im.GetHashCode();
   }
   public static bool operator ==(Complex x, Complex y) 
   {
      return x.re == y.re && x.im == y.im;
   }
   public static bool operator !=(Complex x, Complex y) 
   {
      return !(x == y);
   }
}

A un défaut majeur. Je me réfère à

  public override int GetHashCode() 
   {
      return re.GetHashCode() ^ im.GetHashCode();
   }

XORing est symétrique, donc Complexe (2,1) et Complexe (1,2) donneraient le même hashCode.

Nous devrions probablement faire quelque chose de plus comme:

  public override int GetHashCode() 
   {
      return re.GetHashCode() * 17 ^ im.GetHashCode();
   }
14
Ajk

La plupart du temps, vous pouvez éviter d'implémenter Equals et GetHashcode dans les structures - car il existe une implémentation automatique par le compilateur pour les types Value utilisant le contenu au niveau du bit + la réflexion pour les membres de référence.

Jetez un oeil à ce post: Quel est le meilleur pour le magasin de données Struct/Classes?

Donc, pour une facilité d'utilisation, vous pouvez toujours implémenter == et! =.

Mais la plupart du temps, vous pouvez éviter d'implémenter Equals et GetHashcode.
Un cas où vous auriez à implémenter Equals et GetHashCode est pour un champ que vous ne voulez pas prendre en compte.
Par exemple, un champ qui varie avec le temps, comme l'âge d'une personne ou la vitesse instantanée d'une voiture (l'identité de l'objet ne devrait pas changer si vous voulez le retrouver dans le dictionnaire au même endroit)

Cordialement, meilleur code

9
Emmanuel DURIN

La différence fondamentale entre les deux est que l'opérateur == Est statique, c'est-à-dire que la méthode appropriée à invoquer est déterminée au moment de la compilation, tandis que la méthode Equals est invoquée de manière dinamique sur une instance.
Définir les deux est probablement la meilleure chose à faire, même si cela importe moins dans le cas des structures, car les structures ne peuvent pas être étendues (une structure ne peut pas hériter d'une autre).

3
Paolo Tedesco

Juste pour être complet, je conseillerais également de surcharger la méthode Equals:

public bool Equals(Complex other) 
{
   return other.re == re && other.im == im;
}

il s'agit d'une véritable amélioration car il n'y a pas de boxe de l'argument d'entrée de la méthode Equals(Object obj)

Quelques bonnes pratiques d'utilisation des types de valeur:

  • les rendre immuables
  • remplacer Equals (celui qui prend un objet comme argument);
  • surcharge Equals pour prendre une autre instance du même type de valeur (par exemple * Equals (Complex other));
  • opérateurs de surcharge == et! =;
  • remplacer GetHashCode

Cela vient de ce post: http://theburningmonk.com/2015/07/beware-of-implicit-boxing-of-value-types/

0
Tomasz Jaskuλa