2009-10-01 6 views
57

Comme l'indique le titre: dois-je remplacer l'opérateur ==? que diriez-vous de la méthode .Equals()? Quelque chose que je manque?Qu'est-ce qui doit être remplacé dans une structure pour garantir le bon fonctionnement de l'égalité?

+0

Aussi attention pour http://stackoverflow.com/questions/1972262/c-sharp-okay-with-comparing-value-types-to-null/21962298 - si vous n'êtes pas Attention, la comparaison de votre structure (un type de valeur) à null compilera très bien mais ne fera pas ce que vous attendez. – yoyo

Répondre

70

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); 
    } 
} 
+9

Pour les curieux: http://msdn.microsoft.com/en-us/library/336aedhh(v=VS.71).aspx – Mike

+0

Je me demande s'il ne serait pas préférable d'utiliser 'Complex other = obj comme Complex', puis vérifiez si 'other == null' au lieu d'utiliser' is', puis un cast ... –

+3

@Clement: Vous ne pouvez pas faire cela pour un struct; le résultat ne peut pas être nul. Vous obtiendrez une erreur de compilation. –

3

La différence fondamentale entre les deux est que l'opérateur == est statique, à savoir la méthode appropriée pour appeler est déterminée au moment de la compilation, tandis que la méthode Equals est appelée dinamically sur un exemple.
Définir les deux est probablement la meilleure chose à faire, même si cela importe moins dans le cas des structures, puisque les structures ne peuvent pas être étendues (une structure ne peut pas hériter d'une autre structure).

40

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

Implémenter IEquatable sur les types de valeurs. La méthode Object.Equals sur les types de valeur entraîne la mise en boîte, et son implémentation par défaut n'est pas très effcace car elle utilise la refection. IEquatable.Equals peut offrir de meilleures performances et peut être mis en œuvre afin qu'il ne provoque pas de boxe.

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

DO suivre les mêmes directives que pour Object.Equals prépondérants lorsque de IEquatable.Equals de mise en œuvre. Voir la section 8.7.1 pour directives détaillées sur Object.Equals prépondérants

+0

Donc, cela est seulement utilisé sur les types de valeur? (pas de référence?) – UpTheCreek

+1

Parce que les types de référence n'ont pas besoin d'être encadrés lorsqu'ils sont passés en tant qu'objet, ergo, IEquatable ne fournirait aucun avantage. Les types de valeur sont généralement copiés entièrement sur la pile (ou dans la disposition des types externes), donc pour obtenir une référence d'objet, et gérer correctement la durée de vie de l'objet, il doit être encadré (enveloppé avec un type spécial) et copié au tas; seulement alors la référence à l'objet tas peut être passée à une fonction comme Object.Equals. – gimpf

12

Unfortunetely Je n'ai pas assez réputation pour commenter d'autres entrées. Donc, je poste l'amélioration possible de la meilleure solution ici.

me corriger si je me trompe, mais la mise en œuvre mentionné 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-défaut majeur. Je référant à

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

XOR est symétrique, si complexe (2,1) et complexe (1,2) donnerait même hashCode.

Nous devrions probablement faire quelque chose comme:

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

Avoir des collisions hashcode n'est pas nécessairement un problème. En fait vous aurez toujours une chance de collision (lire sur les trous de pigion/paradoxe d'anniversaire) Dans votre cas Complexe (1,4) et complexe (4,1) entrent en collision (il y a eu moins de collisions) cela dépend de vos données . Le hashcode est utilisé pour éliminer rapidement 99,999% des objets indésirables (par exemple, dans un dictionnaire). Les opérateurs d'égalité ont le dernier mot. – DarcyThomas

+0

Cela a été dit plus vous avez de propriétés sur la structure, il y a une plus grande chance d'une collision. Cela peut être un meilleur algorithme de hachage: http://stackoverflow.com/a/263416/309634 – DarcyThomas

0

Juste pour complétude Je conseille également à une surcharge méthode Equals:

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

c'est une réelle amélioration de spead car il n'y a pas survenant de boxe de l'argument en entrée de Equals(Object obj) méthode

Quelques bonnes pratiques pour utiliser les types de valeur:

  • les rendre immuable
  • override Égal (celle qui prend un objet comme argument); Surcharge
  • surcharge Equivaut à prendre une autre instance du même type de valeur (par exemple * Equals (Complex other));
  • opérateurs de surcharge == et! =;
  • override GetHashCode

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

8

La plupart du temps, vous pouvez éviter la mise en œuvre Equals et GetHashCode dans struct - parce qu'il ya une mise en oeuvre automatique par le compilateur pour les types de valeur à l'aide au niveau du bit contenu + réflexion pour les membres de référence.

Jetez un oeil à ce poste: Which is best for data store Struct/Classes?

Donc, pour la facilité d'utilisation, vous pouvez toujours mettre en œuvre == et =.

Mais la plupart du temps, vous pouvez éviter d'implémenter Equals et GetHashcode.
Un cas où vous devez implémenter Equals et GetHashCode est pour un champ que vous ne voulez pas prendre en compte.
Par exemple un champ qui varie au fil du temps comme Age of a Person ou instantSpeed ​​d'une voiture (l'identité de l'objet ne doit pas changer si vous voulez le retrouver dans le dictionnaire au même endroit)

Cordialement, meilleur code

Questions connexes