Como faço para implementar IEqualityComparer em um struct par genérico imutável?

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

  •  02-07-2019
  •  | 
  •  

Pergunta

Atualmente tenho isso (editado depois de ler o conselho):

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);
    }
}

O problema é que primeiro e segundo pode não ser tipos de referência (VS realmente me avisa sobre isso), mas o código ainda compila. Devo lançá-los (primeira e segunda) a objetos antes de compará-los, ou se existe uma maneira melhor de fazer isso?

Edit: Nota que eu deseja este struct ao valor de apoio e tipos de referência (em outras palavras, restringindo por classe não é uma solução válida)

Edit 2: Quanto ao que eu estou tentando alcançar, eu quero que este trabalho em um dicionário. Em segundo lugar, SRP não é importante para mim agora porque isso não é realmente a essência do problema - ele sempre pode ser reformulado depois. Em terceiro lugar, em comparação com o padrão (T) não irá funcionar em vez de comparar a null -. Experimentá-lo

Foi útil?

Solução

Parece que você precisa IEquatable vez:

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);
    }
  }
}

Outras dicas

A implementação IEqualityComparer deve ser uma classe diferente (e definitivamente, não é um struct como você deseja reutilizar a referência).

Além disso, o hashcode nunca deve ser armazenado em cache, como a implementação GetHashCode padrão para um struct (que você não substituir) terá esse membro em conta.

Se você usar hashcodes na comparação de métodos, você deve verificar se há "valor realmente" se os códigos de hash são os mesmos.

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;

Mas há problema littlebit com comparando "_first" e campos "_second". Por padrão tipos de referência usos igualdade tona comparando "Object.ReferenceEquals" Método, broto eles podem substituí-los. Portanto, a solução correta depende do "o que exatamente deve fazer" o seu método de comparação. Deve usar "é igual a" método dos "_first" e campos "_second", ou Object.ReferenceEquals? Ou algo mais complexo?

Em relação ao aviso, você pode usar default (T) e padrão (K) em vez de null.

Eu não posso ver o que você está tentando alcançar, mas você não deve estar usando o código hash para comparar a igualdade - não há garantia de que dois objetos diferentes não terão o mesmo código hash. Além disso, embora a sua estrutura é imutável, os membros _first e _second não são.

Em primeiro lugar este código viola o princípio SRP. classe par usado para pares preensão se os itens, certo? É incorreta a delegar a igualdade comparando funcionalidade a ele.

Em seguida, vamos dar uma olhada em seu código:

Igual método irá falhar se um dos argumentos é nulo - não é bom. Igual código usa hash da classe Pair, mas dê uma olhada na definição de GetHashCode, é apenas uma combinação de pares de códigos de membros de hash - é não tem nada a ver com a igualdade de itens. Eu esperaria que iguala o método irá comparar os dados reais. Estou muito ocupado no momento de fornecer uma aplicação correcta, infelizmente. Mas desde o primeiro olhar, você código parece estar errado. Seria melhor se você nos fornecer descrição do que você deseja alcançar. Tenho certeza de que os membros assim será capaz de dar-lhe alguns conselhos.

Posso sugerir o uso de expressões lambda como um parâmetro? isso permitiria que você especifique como comparar os tipos genéricos internos.

Eu não receber qualquer aviso ao compilar sobre isso, mas eu suponho que você está falando sobre a comparação == null? Um elenco parece que ele iria fazer isso mais limpo todos um pouco, sim.

PS. Você realmente deve usar uma classe separada para o comparador. Esta classe que preenche duas funções (sendo um par e comparando pares) é feio.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top