¿Cómo implemento IEqualityComparer en una estructura de par genérica inmutable?

StackOverflow https://stackoverflow.com/questions/120783

  •  02-07-2019
  •  | 
  •  

Pregunta

Actualmente tengo esto (editado después de leer los consejos):

struct Pair<T, K> : IEqualityComparer<Pair<T, K>>
{
    readonly private T _first;
    readonly private K _second;

    public Pair(T first, K second)
    {
        _first = first;
        _second = second;

    }

    public T First { get { return _first; } }
    public K Second { get { return _second; } }

    #region IEqualityComparer<Pair<T,K>> Members

    public bool Equals(Pair<T, K> x, Pair<T, K> y)
    {
        return x.GetHashCode(x) == y.GetHashCode(y);
    }

    public int GetHashCode(Pair<T, K> obj)
    {
        int hashCode = obj.First == null ? 0 : obj._first.GetHashCode();

        hashCode ^= obj.Second == null ? 0 : obj._second.GetHashCode();

        return hashCode;
    }

    #endregion

    public override int GetHashCode()
    {
        return this.GetHashCode(this);
    }

    public override bool Equals(object obj)
    {
        return (obj != null) && 
    (obj is Pair<T, K>) && 
    this.Equals(this, (Pair<T, K>) obj);
    }
}

El problema es que es posible que Primero y Segundo no sean tipos de referencia (VS en realidad me advierte sobre esto), pero el código aún se compila.¿Debo convertirlos (Primero y Segundo) en objetos antes de compararlos, o hay una mejor manera de hacerlo?

Editar:Tenga en cuenta que yo desear esta estructura para admitir tipos de valor y referencia (en otras palabras, restringir por clase no es una solución válida)

Edición 2:En cuanto a lo que intento lograr, quiero que esto funcione en un Diccionario.En segundo lugar, SRP no es importante para mí en este momento porque esa no es realmente la esencia de este problema; siempre se puede refactorizar más adelante.En tercer lugar, comparar con el valor predeterminado (T) no funcionará en lugar de comparar con nulo; pruébelo.

¿Fue útil?

Solución

Parece que necesitas IEquatable en su lugar:

internal struct Pair<T, K> : IEquatable<Pair<T, K>>
{
  private readonly T _first;
  private readonly K _second;

  public Pair(T first, K second)
  {
    _first = first;
    _second = second;
  }

  public T First
  {
    get { return _first; }
  }

  public K Second
  {
    get { return _second; }
  }

  public bool Equals(Pair<T, K> obj)
  {
    return Equals(obj._first, _first) && Equals(obj._second, _second);
  }

  public override bool Equals(object obj)
  {
    return obj is Pair<T, K> && Equals((Pair<T, K>) obj);
  }

  public override int GetHashCode()
  {
    unchecked
    {
      return (_first != null ? _first.GetHashCode() * 397 : 0) ^ (_second != null ? _second.GetHashCode() : 0);
    }
  }
}

Otros consejos

Su implementación de IEqualityComparer debería ser una clase diferente (y definitivamente no una estructura, ya que desea reutilizar la referencia).

Además, su código hash nunca debe almacenarse en caché, ya que la implementación predeterminada de GetHashcode para una estructura (que no anula) tendrá en cuenta a ese miembro.

Si utiliza códigos hash al comparar métodos, debe verificar el "valor real" si los códigos hash son los mismos.

bool result = ( x._hashCode == y._hashCode );
if ( result ) { result = ( x._first == y._first && x._second == y._second ); }
// OR?: if ( result ) { result = object.Equals( x._first, y._first ) && object.Equals( x._second, y._second ); }
// OR?: if ( result ) { result = object.ReferenceEquals( x._first, y._first ) && object.Equals( x._second, y._second ); }
return result;

Pero hay un pequeño problema al comparar los campos "_first" y "_ second".De forma predeterminada, los tipos de referencia utilizan el método "object.ReferenceEquals" para comparar la igualdad, pero pueden anularlos.Por lo tanto, la solución correcta depende de "qué se debe hacer exactamente" en su método de comparación.¿Debería utilizar el método "Equals" de los campos "_first" y "_ second", u object.ReferenceEquals?¿O algo más complejo?

Con respecto a la advertencia, puede usar default(T) y default(K) en lugar de nulo.

No veo lo que estás tratando de lograr, pero no deberías usar el código hash para comparar la igualdad; no hay garantía de que dos objetos diferentes no tengan el mismo código hash.Además, aunque su estructura es inmutable, los miembros _first y _ second no lo son.

En primer lugar, este código viola el principio SRP.La clase de par solía contener pares de elementos, ¿verdad?Es incorrecto delegar la igualdad comparándole la funcionalidad.

A continuación, echemos un vistazo a su código:

El método igual fallará si uno de los argumentos es nulo, no es bueno.Equals usa el código hash de la clase Pair, pero eche un vistazo a la definición de GetHashCode, es solo una combinación de códigos hash de los miembros del par; no tiene nada que ver con la igualdad de elementos.Esperaría que el método Equals compare los datos reales.Desafortunadamente, estoy demasiado ocupado en este momento para proporcionar una implementación correcta.Pero desde el primer vistazo, su código parece estar incorrecto.Sería mejor si nos proporcionas una descripción de lo que quieres lograr.Estoy seguro de que los miembros de SO podrán darte algunos consejos.

¿Puedo sugerir el uso de expresiones Lambda como parámetro?esto le permitiría especificar cómo comparar los tipos genéricos internos.

No recibo ninguna advertencia al compilar sobre esto, pero supongo que estás hablando de la comparación == nula.Un yeso parece que haría todo esto un poco más limpio, sí.

PD.Realmente deberías usar una clase separada para el comparador.Esta clase que cumple dos roles (ser una pareja y comparar parejas) es simplemente fea.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top