Quero entender os cenários onde IEqualityComparer<T>
e IEquatable<T>
deve ser usado. A documentação do MSDN para os dois parece muito semelhante.
c#
.net
equals
iequalitycomparer
iequatable
Tilak
fonte
fonte
EqualityComparer<T>
vez de implementar a interface "porqueEqualityComparer<T>
testa a igualdade usandoIEquatable<T>
T
implementaçãoIEquatable<T>
.List<T>
Caso contrário, uma coleção teria algum tipo de bug sutil?Respostas:
IEqualityComparer<T>
é uma interface para um objecto que executa a comparação de dois objectos do tipoT
.IEquatable<T>
é para um objeto do tipoT
para que ele possa se comparar com outro do mesmo tipo.fonte
IEqualityComparer<T>
é uma interface para um objeto (que geralmente é uma classe leve diferente deT
) que fornece funções de comparação que operamT
Ao decidir se deve usar
IEquatable<T>
ouIEqualityComparer<T>
, pode-se perguntar:Se houver apenas uma maneira de testar duas instâncias de
T
igualdade, ou se um dos vários métodos for preferido, entãoIEquatable<T>
seria a escolha certa: Essa interface deve ser implementada apenas porT
si só, de modo que uma instânciaT
tenha conhecimento interno de como se comparar a outra instância deT
.Por outro lado, se houver vários métodos igualmente razoáveis de comparar dois
T
s para igualdade,IEqualityComparer<T>
pareceria mais apropriado: Essa interface não deve ser implementada porT
ela mesma, mas por outras classes "externas". Portanto, ao testar duas instâncias deT
igualdade, porT
não ter um entendimento interno da igualdade, você terá que fazer uma escolha explícita de umaIEqualityComparer<T>
instância que executa o teste de acordo com seus requisitos específicos.Exemplo:
Vamos considerar esses dois tipos (que deveriam ter semântica de valor ):
Por que apenas um desses tipos herdaria
IEquatable<>
, mas não o outro?Em teoria, existe apenas uma maneira sensata de comparar duas instâncias de qualquer tipo: elas são iguais se a
X
eY
propriedades em ambas as instâncias forem iguais. De acordo com esse pensamento, os dois tipos devem implementarIEquatable<>
, porque não parece provável que haja outras maneiras significativas de se fazer um teste de igualdade.O problema aqui é que comparar números de ponto flutuante por igualdade pode não funcionar como esperado, devido a erros de arredondamento de minutos . Existem métodos diferentes de comparar números de ponto flutuante para quase-igualdade , cada um com vantagens e vantagens específicas, e você pode querer escolher qual método é apropriado.
Observe que a página à qual vinculei (acima) declara explicitamente que esse teste de quase igualdade tem alguns pontos fracos. Como essa é uma
IEqualityComparer<T>
implementação, você pode simplesmente trocá-la se não for boa o suficiente para seus propósitos.fonte
IEqualityComparer<T>
implementação legítima deve implementar uma relação de equivalência, o que implica que em todos os casos em que dois objetos se comparam a um terço, eles devem ser comparados. Qualquer classe que implementeIEquatable<T>
ouIEqualityComparer<T>
de uma maneira contrária ao acima é quebrada.IEqualityComparer<String>
que considera "olá" igual a "Olá" e "hElLo" deve considerar "Olá" e "hElLo" iguais entre si, mas para a maioria dos métodos de comparação isso não seria um problema.GetHashCode
você deve determinar rapidamente se dois valores diferem. As regras são basicamente as seguintes: (1)GetHashCode
sempre deve produzir o mesmo código de hash para o mesmo valor. (2)GetHashCode
deve ser rápido (mais rápido queEquals
). (3)GetHashCode
não precisa ser preciso (não tão preciso quantoEquals
). Isso significa que ele pode produzir o mesmo código de hash para valores diferentes. Quanto mais preciso você conseguir, melhor, mas provavelmente é mais importante mantê-lo rápido.Você já tem a definição básica do que são . Em resumo, se você implementar
IEquatable<T>
na classeT
, oEquals
método em um objeto do tipoT
informará se o próprio objeto (aquele que está sendo testado quanto à igualdade) é igual a outra instância do mesmo tipoT
. Visto que,IEqualityComparer<T>
é para testar a igualdade de quaisquer duas instâncias deT
, normalmente fora do escopo das instâncias deT
.Quanto ao que eles servem pode ser confuso no começo. A partir da definição, deve ficar claro que, portanto
IEquatable<T>
(definido naT
própria classe ) deve ser o padrão de fato para representar a exclusividade de seus objetos / instâncias.HashSet<T>
,Dictionary<T, U>
(ConsiderandoGetHashCode
é substituído tão bem),Contains
sobreList<T>
etc fazer uso deste. ImplementaçãoIEqualityComparer<T>
emT
não ajuda os casos gerais acima mencionados. Posteriormente, há pouco valor para implementarIEquatable<T>
em qualquer outra classe que não sejaT
. Este:raramente faz sentido.
Por outro lado
é assim que deve ser feito.
IEqualityComparer<T>
pode ser útil quando você precisar de uma validação personalizada da igualdade, mas não como regra geral. Por exemplo, em uma classe dePerson
em algum momento você pode precisar testar a igualdade de duas pessoas com base na idade delas. Nesse caso, você pode fazer:Para testá-los, tente
Da mesma forma
IEqualityComparer<T>
emT
não faz sentido.É verdade que isso funciona, mas não parece bom para os olhos e derrota a lógica.
Geralmente o que você precisa é
IEquatable<T>
. Idealmente, você pode ter apenas um,IEquatable<T>
enquanto múltiplosIEqualityComparer<T>
são possíveis com base em critérios diferentes.O
IEqualityComparer<T>
eIEquatable<T>
são exatamente análogosComparer<T>
eIComparable<T>
que são usados para fins de comparação ao invés de equacionar; um bom tópico aqui onde escrevi a mesma resposta :)fonte
public int GetHashCode(Person obj)
deve retornarobj.GetHashCode()
obj.Age.GetHashCode
. irá editar.person.GetHashCode
lugar nenhum .... por que você o substituiu? - Substituímos porque o objetivo principalIEqualityComparer
é ter uma implementação de comparação diferente de acordo com nossas regras - as regras que realmente conhecemos bem.Age
propriedade, então ligoAge.GetHashCode
. A idade é do tipoint
, mas qualquer que seja o tipo de contrato de código hash no .NET é essedifferent objects can have equal hash but equal objects cant ever have different hashes
. Este é um contrato em que podemos acreditar cegamente. Certamente, nossos comparadores personalizados também devem seguir essa regra. Se alguma vez a chamadasomeObject.GetHashCode
quebra as coisas, então é o problema com os implementadores do tipo desomeObject
, não é o nosso problema.IEqualityComparer é para uso quando a igualdade de dois objetos é implementada externamente, por exemplo, se você deseja definir um comparador para dois tipos para os quais você não tem a fonte, ou para casos em que a igualdade entre duas coisas só faz sentido em algum contexto limitado.
IEquatable é para o próprio objeto (aquele que está sendo comparado em termos de igualdade) para implementar.
fonte
Um compara dois
T
s. O outro pode se comparar a outrosT
s. Normalmente, você só precisará usar um de cada vez, não os dois.fonte