2009-06-08 3 views
4

J'ai une classe qui ressemble à ceci:Question sur le Dictionnaire <T,T>

public class NumericalRange:IEquatable<NumericalRange> 
    { 
     public double LowerLimit; 
     public double UpperLimit; 

     public NumericalRange(double lower, double upper) 
     { 
      LowerLimit = lower; 
      UpperLimit = upper; 
     } 

     public bool DoesLieInRange(double n) 
     { 
      if (LowerLimit <= n && n <= UpperLimit) 
       return true; 
      else 
       return false; 
     } 

     #region IEquatable<NumericalRange> Members 

     public bool Equals(NumericalRange other) 
     { 
      if (Double.IsNaN(this.LowerLimit)&& Double.IsNaN(other.LowerLimit)) 
      { 
       if (Double.IsNaN(this.UpperLimit) && Double.IsNaN(other.UpperLimit)) 
       { 
        return true; 
       } 
      } 

      if (this.LowerLimit == other.LowerLimit && this.UpperLimit == other.UpperLimit) 
       return true; 
      return false; 
     } 

     #endregion 
    } 

Cette classe contient une gamme neumerical de valeurs. Cette classe doit également être capable de contenir une plage par défaut, où LowerLimit et UpperLimit sont égales à Double.NaN.

Maintenant cette classe va dans un dictionnaire

Le dictionnaire fonctionne très bien pour les valeurs de plage numérique "non-NAN, mais quand la clé est {NaN, NaN} objet NumericalRange, le dictionnaire jette un KeyNotFoundException.

Qu'est-ce que je fais mal? Y a-t-il une autre interface que je dois implémenter? En fonction de votre commentaire, vous n'avez pas implémenté GetHashCode.

+1

Avez-vous également remplacé GetHashCode et Equals (obj)? – Rauhotz

+0

Non. Ces remplacements sont-ils nécessaires? Si oui, comment implémentez-vous GetHashCode? – ashwnacharya

+1

Voir: http://stackoverflow.com/questions/371328/why-is-it-important-to-override-gethashcode-when-equals-method-is-overriden-in-c/371348#371348 –

Répondre

12

Je suis étonné que la classe fonctionne tout dans un dictionnaire, à moins que vous êtes toujours demandé que vous mettez dans la clé identique Je suggère une mise en œuvre de quelque chose comme:.

public override int GetHashCode() 
{ 
    int hash = 17; 
    hash = hash * 23 + UpperLimit.GetHashCode(); 
    hash = hash * 23 + LowerLimit.GetHashCode(); 
    return hash; 
} 

Ce supposeDouble.GetHashCode() donne une valeur constante pour NaN. Il ya bien sûr beaucoup de valeurs de NaN, et vous voulez cas particulier pour s'assurer qu'ils donnent tous le même hachage.

Vous devez également remplacer la méthode héritée de EqualsObject:

public override bool Equals(Object other) 
{ 
    return other != null && 
      other.GetType() == GetType() && 
      Equals((NumericalRange) other); 
} 

Notez que la vérification de type peut être rendu plus efficace en utilisant as si vous scellez votre classe. Sinon, vous obtiendrez des asymétries intéressantes entre x.Equals(y) et y.Equals(x) si quelqu'un dérive une autre classe de la vôtre. L'égalité devient difficile avec l'héritage.

Vous devriez également rendre vos champs privés, en les exposant seulement comme des propriétés. Si cela va être utilisé comme une clé dans un dictionnaire, je vous recommande vivement de le faire en lecture seule aussi. Changer le contenu d'une clé quand elle est utilisée dans un dictionnaire risque de la rendre «indisponible» plus tard.

+4

Jon F &% $ ing Skeet !! C'est comme demander des indications au Vatican et être répondu par le pape !!!! http://preview.tinyurl.com/5va9gs – ashwnacharya

+0

Question stupide, pourquoi 17 & 23? Est-ce que ces chiffres sont arbitraires ou y a-t-il un certain nombre de raisons? – Chris

+0

Deviner, nombres premiers? – ashwnacharya

7

L'implémentation par défaut de la méthode GetHashCode utilise la référence de l'objet plutôt que les valeurs de l'objet. Vous devez utiliser la même instance de l'objet que vous avez utilisé pour mettre les données dans le dictionnaire pour que cela fonctionne.

Une mise en œuvre de GetHashCode qui fonctionne simplement crée un code à partir des codes de hachage de celui-ci est membres de données:

public int GetHashCode() { 
    return LowerLimit.GetHashCode()^UpperLimit.GetHashCode(); 
} 

(ce qui est la même implémentation que la structure Point utilise.)

Toute mise en œuvre de la méthode qui renvoie toujours le même code de hachage pour toutes les valeurs de paramètres données fonctionne lorsqu'elle est utilisée dans un dictionnaire. Le simple retour du même code de hachage pour toutes les valeurs fonctionne également, mais la performance du dictionnaire est mauvaise (la recherche d'une clé devient une opération O (n) au lieu d'une opération O (1). méthode devrait distribuer les codes de hachage uniformément dans la gamme.

Si vos données sont fortement biaisées, la mise en œuvre ci-dessus peut ne pas donner les meilleures performances. Si vous avez par exemple beaucoup de plages où les limites inférieure et supérieure sont les mêmes, ils auront tous le code de hachage zéro. Dans ce cas quelque chose comme cela pourrait fonctionner mieux:

public int GetHashCode() { 
    return (LowerLimit.GetHashCode() * 251)^UpperLimit.GetHashCode(); 
} 

Vous devriez envisager de faire la classe immuable, à savoir rendre ses propriétés en lecture seule et ne les mettre dans le constructeur. Si vous modifiez les propriétés d'un objet alors qu'il se trouve dans un dictionnaire, son code de hachage va changer et vous ne pourrez plus accéder à l'objet.

+5

Je sais ce que ça fait d'avoir répondu correctement et pouffé par la grandeur de "The Skeet" +1 :) – Perpetualcoder

+0

@Guffa Je suis curieux de connaître votre deuxième bloc de code. Avec cette implémentation, si vous avez beaucoup de plages où les limites supérieure et inférieure sont les mêmes, vous obtiendrez toujours le même code de hachage, n'est-ce pas? Ce sera juste non nul. Y a-t-il un avantage à avoir un code de hachage non nul, mais fortement dupliqué par rapport aux valeurs hautement dupliquées de zéro? – JMarsch

+1

@JMarsch: La raison pour laquelle la première implémentation donne le même code de hachage est que x^x donne toujours la même valeur indépendamment de la valeur de x, à savoir zéro. (x * 251)^x donne des résultats différents pour différentes valeurs de x. – Guffa

Questions connexes