2012-06-29 3 views
5

J'implémente un DoubleEqualityComparer réutilisable (avec une tolérance personnalisée: le paramètre constructeur "epsilon") pour faciliter l'utilisation de LINQ avec des séquences de double. Par exemple:IEqualityComparer <double> avec une tolérance; comment implémenter GetHashCode?

bool myDoubleFound = doubles.Contains(myDouble, new DoubleEqualityComparer(epsilon: 0.01)); 

Quelle est la bonne façon d'implémenter GetHashCode? Voici le code:

public class DoubleEqualityComparer : IEqualityComparer<double>, IEqualityComparer<double?> 
    { 
     private readonly double epsilon; 

     public DoubleEqualityComparer(double epsilon) 
     { 
      if (epsilon < 0) 
      { 
       throw new ArgumentException("epsilon can't be negative", "epsilon"); 
      } 

      this.epsilon = epsilon; 
     } 

     public bool Equals(double x, double y) 
     { 
      return System.Math.Abs(x - y) < this.epsilon; 
     } 

     public int GetHashCode(double obj) 
     { 
      // ? 
     } 
    } 

PS: Je peux toujours retourner la même valeur (ex: GetHashCode (Double obj) {return 0;}) pour forcer toujours l'appel à Equals méthode (à double, double) (pas très performant, je sais), mais je me souviens que cette solution cause des problèmes quand le comparateur est utilisé avec un dictionnaire ...

+8

Vous ne devriez pas faire cela parce que cela viole la transitivité. Il est possible que 'a est égal à b' et' b 'est égal à c' mais 'a not n'est pas égal à c'. – Ani

Répondre

4

Je ne suis pas sûr d'utiliser EqualityComparer. Parce que les objets comparés ne sont pas égaux.

Peut-être que vous devriez envisager d'utiliser d'une simple clause Any + une méthode utilitaire:

private static bool DoublesAreNearlyEquals(double d1, double d2, double epsilon = 0.01D) 
{ 
    return System.Math.Abs(d1 - d2) < this.epsilon; 
} 

private void foo() 
{ 
    var myDoubles = Getdoubles(); 
    var doubleToSearch = 42D; 
    var result = myDoubles.Any(d=>DoublesAreNearlyEquals(d, doubleToSearch)); 
} 
+1

Merci, vous et Ani m'a convaincu de ne pas utiliser IEqualityComparer, mais pour définir une interface personnalisée (et son ensemble de méthodes d'extension, LINQ style): interface publique ITolerable { bool AreAlmostEqual (T x, T y) ; IEqualityComparer était pratique car il y a des méthodes officielles LINQ prêtes à l'emploi (qui n'appellent pas GetHashcode), mais je ne me suis pas gêné pour le laisser non implémenté était horrible (et dangereux). – Notoriousxl

1

Je jetterais NotSupportedException dans GetHashCode de sorte que vous pouvez avoir votre gâteau et le manger aussi. Cela vous donne la commodité d'avoir un IEqualityComparer dans LINQ et d'autres méthodes, mais garantit que toute utilisation de GetHashCode explose. En pratique, vous constaterez peut-être que l'utilisation du comparateur d'égalité ne nécessite pas l'appel de GetHashCode. Vous pourriez même appeler cette classe NotHashableDoubleEqualityComparer pour être super clair sur la limitation aux appelants.

Questions connexes