2012-06-29 19 views
5

Wdrażam wielokrotnego użytku DoubleEqualityComparer (z niestandardową tolerancją: parametr konstruktora "epsilon"), aby ułatwić korzystanie z LINQ z sekwencjami podwójnymi. Na przykład:IEqualityComparer <double> z tolerancją; jak zaimplementować GetHashCode?

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

Jaki jest właściwy sposób na wdrożenie GetHashCode? Oto kod:

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: zawsze mogę wrócić tą samą wartość (np GetHashCode (podwójne obj) {return 0;}) zawsze wymusić wywołanie equals (podwójne podwójne,) metoda (nie bardzo performant, wiem), ale pamiętam, że to rozwiązanie powoduje problemy, gdy porównywarka jest używana ze słownikiem ...

+8

Nie powinieneś tego robić, ponieważ narusza przechodniość. Możliwe, że 'a równa się b' i' b równa się c', ale 'a nie równa się c'. – Ani

Odpowiedz

4

Nie jestem pewien, czy korzystanie z EqualityComparer jest drogą do zrobienia. Ponieważ porównywane obiekty nie są równe.

Może należy rozważyć użycie prostego Any klauzuli + metodę użytkowy:

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

Dzięki, ty i Ani przekonał mnie, aby nie używać IEqualityComparer, ale zdefiniować niestandardowy interfejs (i jego zestaw metod rozszerzających, LINQ-style): public interface ITolerable { bool AreAlmostEqual (T x T y) ; } IEqualityComparer był przydatny, ponieważ istnieją oficjalne metody LINQ gotowe do użycia (które nie nazywają się GetHashcode), ale nie podoba mi się pozostawić to niezaimplementowane było okropne (i niebezpieczne). – Notoriousxl

1

chciałbym rzucać NotSupportedException w GetHashCode, dzięki czemu można mieć ciastko i je zjeść. Daje to wygodę posiadania IEqualityComparer w LINQ i innych metodach, ale gwarantuje, że każde użycie GetHashCode wysadzi w powietrze. W praktyce może się okazać, że sposób, w jaki korzystasz z porównywania równości, nigdy nie wymaga wywołania GetHashCode. Możesz nawet nazwać tę klasę NotHashableDoubleEqualityComparer, aby być super jasnym co do ograniczenia dla osób dzwoniących.