2012-06-29 19 views
5

Estoy implementando un DoubleEqualityComparer reutilizable (con una tolerancia personalizada: el parámetro constructor "epsilon") para facilitar el uso de LINQ con secuencias de doble. Por ejemplo:IEqualityComparer <double> con una tolerancia; cómo implementar GetHashCode?

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

¿Cuál es la forma correcta de implementar GetHashCode? Aquí está el código:

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) 
     { 
      // ? 
     } 
    } 

PD: siempre puede volver el mismo valor (por ejemplo: GetHashCode (doble obj) {return 0;}) para forzar siempre la llamada al método equals (doble, doble) (no muy rendimiento, lo sé), pero recuerdo que esta solución causa problemas cuando el comparador se utiliza con un diccionario ...

+8

No debe hacer esto porque viola la transitividad. Es posible que 'a sea igual a b' y' b sea igual a c' pero 'a no sea igual a c'. – Ani

Respuesta

4

No estoy seguro de que el uso de EqualityComparer sea el camino a seguir. Porque los objetos comparados no son iguales.

Tal vez debería considerar el uso de un simple Any cláusula + un método de utilidad:

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

Gracias, usted y Ani me convenció de no utilizar IEqualityComparer, sino definir una interfaz personalizada (y su conjunto de métodos de extensión, LINQ-estilo): interfaz pública ITolerable { bool AreAlmostEqual (T x, t y) ; } IEqualityComparer fue útil porque hay métodos oficiales de LINQ listos para usar (que no llaman GetHashcode), pero entiendo que dejarlo no implementado fue horrible (y peligroso). – Notoriousxl

1

que lanzaba NotSupportedException en GetHashCode lo que puede tener su pastel y comérselo también. Esto le brinda la comodidad de tener un IEqualityComparer en LINQ y otros métodos, pero garantiza que cualquier uso de GetHashCode explota. En la práctica, puede encontrar que la forma en que utiliza el comparador de igualdad nunca requiere que se llame al GetHashCode. Incluso puede llamar a esta clase NotHashableDoubleEqualityComparer para ser muy claro acerca de la limitación para las personas que llaman.

Cuestiones relacionadas