2012-06-29 14 views
5

Sto implementando un DoubleEqualityComparer riutilizzabile (con una tolleranza personalizzata: il parametro del costruttore "epsilon") per facilitare l'utilizzo di LINQ con sequenze di doppio. Ad esempio:IEqualityComparer <double> con tolleranza; come implementare GetHashCode?

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

Qual è il modo corretto per implementare GetHashCode? Ecco il codice:

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: posso sempre tornare lo stesso valore (es: GetHashCode (doppia obj) {return 0;}) per forzare sempre la chiamata al metodo equals (doppio, camera) (non molto performante, lo so), ma ricordo che questa soluzione causa problemi quando il comparatore viene usato con un dizionario ...

+8

Non dovresti farlo perché viola la transitività. È possibile che 'a uguale a b' e' b uguale a c' ma 'a non uguale a c'. – Ani

risposta

4

Non sono sicuro che usare EqualityComparer sia la strada da percorrere. Perché gli oggetti confrontati non sono uguali.

Forse si dovrebbe considerare l'utilizzo di un semplice Any clausola di + un metodo di utilità:

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

Grazie, e Ani ha convinto non me di utilizzare IEqualityComparer, ma per definire un'interfaccia personalizzata (e la sua serie di metodi di estensione, LINQ-style): interfaccia pubblica ITolerable { bool AreAlmostEqual (T x, T y) ; } IEqualityComparer era comodo perché ci sono metodi ufficiali LINQ pronti per l'uso (che non chiamano GetHashcode), ma ho capito che lasciarlo non implementato era terribile (e pericoloso). – Notoriousxl

1

vorrei buttare NotSupportedException in GetHashCode così si può avere la botte piena e la moglie ubriaca. Questo ti dà la comodità di avere un IEqualityComparer in LINQ e altri metodi, ma garantisce che qualsiasi utilizzo di GetHashCode esploda. In pratica, potresti scoprire che il modo in cui usi il comparatore di uguaglianza non richiede mai la chiamata di GetHashCode. Potresti anche chiamare questa classe NotHashableDoubleEqualityComparer per essere molto chiara sulla limitazione dei chiamanti.