Pergunta

Eu li sobre 10 perguntas diferentes sobre quando e como substituir GetHashCode mas ainda há algo que eu não começ completamente. A maioria das implementações de GetHashCode são baseadas nos códigos de hash dos campos do objeto, mas ele foi citado que o valor de GetHashCode nunca deve mudar ao longo da vida útil do objeto. Como é que o trabalho se os campos que ele é baseado em são mutáveis? Também o que se eu quiser dicionário pesquisas, etc, para ser baseada na igualdade de referência não minha Equals substituído?

Eu estou substituindo principalmente Equals para a facilidade de unidade testar meu código de serialização que eu assumo serialização e desserialização (para XML no meu caso) mata a igualdade de referência assim que eu quero certificar-se de, pelo menos, ele está correto pela igualdade de valor. É este má prática para substituir Equals neste caso? Basicamente na maior parte do código em execução Quero igualdade de referência e eu sempre uso == e eu não estou substituindo isso. Devo apenas criar uma nova ValueEquals método ou algo em vez de substituir Equals? Eu costumava assumir que o quadro sempre usa == e não Equals para comparar as coisas e então eu pensei que era seguro Equals substituição, uma vez que me pareceu o seu objectivo era para se você quiser ter um segundo definição de igualdade que é diferente do == operador. Da leitura várias outras questões embora pareça que não é o caso.

EDIT:

Parece minhas intenções não eram claras, o que quero dizer é que 99% do tempo eu quero simples igualdade de referência de idade, comportamento padrão, sem surpresas. Para casos muito raros Quero ter igualdade de valor, e eu quero explicitamente pedido valor igualdade usando .Equals vez de ==.

Quando eu fizer isso o compilador recomenda I GetHashCode substituição, bem como, e é assim que esta questão surgiu. Parecia que há contradizendo metas para GetHashCode quando aplicado a objetos mutáveis, aqueles que estão sendo:

  1. Se a.Equals(b) então a.GetHashCode() deve == b.GetHashCode().
  2. O valor da a.GetHashCode() nunca deve mudar para a vida do a.

Estas parecem naturalmente contradizendo quando um objeto mutável, porque se o estado do objeto mudanças, esperamos que o valor da .Equals() a mudança, o que significa que GetHashCode devem mudar para coincidir com a mudança na .Equals(), mas GetHashCode não deve mudar.

Por que parece haver essa contradição? São estas recomendações não pretende aplicar para objetos mutáveis? Provavelmente assumido, mas pode valer a pena mencionar que eu estou me referindo a não classes estruturas.

Solução:

Estou marcação JaredPar como aceite, mas principalmente para a interação comentários. Para resumir o que eu aprendi com isso é que a única maneira de alcançar todos os objetivos e evitar um possível comportamento peculiar em casos extremos é a única Equals override e GetHashCode baseado em campos imutáveis, ou implementar IEquatable. Este tipo de parece diminuir a utilidade de substituir Equals para tipos de referência, como pelo que tenho visto a maioria dos tipos de referência geralmente não têm campos imutáveis ??a menos que eles são armazenados em um banco de dados relacional para identificá-los com as suas chaves primárias.

Foi útil?

Solução

Como é que isso funciona se os campos que ele é baseado em são mutáveis?

Não no sentido de que o código de hash irá mudar à medida que o objeto muda. Isso é um problema para todos os motivos listados nos artigos que você lê. Infelizmente, este é o tipo de problema que normalmente só aparecem em casos de canto. Assim, os desenvolvedores tendem a fugir com o mau comportamento.

Também o que se eu quiser dicionário pesquisas, etc, para ser baseada na igualdade de referência não meus equals?

Enquanto você implementar uma interface como IEquatable<T> isso não deve ser um problema. A maioria das implementações de dicionário irá escolher um comparador de igualdade de uma forma que vai usar IEquatable<T> sobre Object.ReferenceEquals. Mesmo sem IEquatable<T>, a maioria será o padrão para chamar Object.Equals (), que irá, em seguida, vão para a sua implementação.

Basicamente na maior parte do código em execução Quero igualdade de referência e eu sempre usar == e eu não estou substituindo isso.

Se você espera que seus objetos de comportar-se com a igualdade de valor você deve substituir == e! = Para fazer valer a igualdade de valor para todas as comparações. Os usuários ainda podem usar Object.ReferenceEquals se eles realmente querem igualdade de referência.

Eu costumava assumir que o quadro sempre usa == e não é igual a comparar coisas

O que os usos BCL mudou um pouco ao longo do tempo. Agora a maioria dos casos que a igualdade uso terá uma instância IEqualityComparer<T> e usá-lo para a igualdade. Nos casos onde não é especificado que irá utilizar EqualityComparer<T>.Default para encontrar um. No pior dos casos este será o padrão para chamar Object.Equals

Outras dicas

Se você tem um objeto mutável, não há muito sentido em substituir o método GetHashCode, como você não pode realmente usá-lo. É usado, por exemplo, pelas coleções Dictionary e HashSet para colocar cada item em um balde. Se você alterar o objeto enquanto ele é usado como uma chave na coleção, o código hash não corresponde mais o balde que o objeto se encontra, de modo que a coleção não funciona corretamente e você nunca pode encontrar o objeto novamente.

Se você quiser a pesquisa não usar o GetHashCode ou Equals método da classe, você sempre pode fornecer sua própria implementação IEqualityComparer para usar em vez quando você cria o Dictionary.

O método Equals destina-se a igualdade de valor, por isso não é errado para implementá-lo dessa forma.

Uau, isso é realmente várias perguntas em um :-). Então, um após o outro:

tem sido citado que o valor de GetHashCode nunca deve mudar ao longo da vida útil do objeto. Como é que o trabalho se os campos que ele é baseado em são mutáveis?

Este conselho comum é para o caso em que você deseja usar o objeto como uma chave em um HashTable / dicionário etc. Hashtables geralmente exigem o hash não à mudança, porque eles usá-lo para decidir como armazenar e recuperar a chave. Se as mudanças de hash, o HashTable provavelmente não encontrar o seu objeto.

Para citar os docs de Java do Mapa de interface:

Nota: grande cuidado deve ser exercido se objetos mutáveis ??são utilizados como chaves de mapa. O comportamento de um mapa não é especificado se o valor de um objeto é alterado de uma forma que afeta igual comparações enquanto o objeto é uma chave no mapa.

Em geral, é uma má idéia para uso qualquer tipo de objeto mutável como uma chave em uma tabela hash: Não é ainda claro o que deve acontecer se um principais mudanças depois de ter sido adicionada à tabela de hash . Caso a tabela hash devolver o objeto armazenado através da tecla de idade, ou através da nova chave, ou através de ambos?

Assim, o conselho real é: Só use objetos imutáveis ??como chaves, e certifique-se a sua hashcode nunca muda qualquer um (que normalmente é automática se o objeto é imutável)

.

Também o que se eu quiser dicionário pesquisas, etc, para ser baseada na igualdade de referência não meus equals?

Bem, encontrar uma implementação de dicionário que funciona assim. Mas os dicionários da biblioteca padrão usar o hashcode & Equals, e não há nenhuma maneira de mudar isso.

Eu estou substituindo principalmente Equals para a facilidade de unidade testar meu código de serialização que eu assumo serialização e desserialização (para XML no meu caso) mata a igualdade de referência assim que eu quero certificar-se de, pelo menos, ele está correto pela igualdade de valor. É este má prática para substituir Equals neste caso?

Não, eu acho que é perfeitamente aceitável. No entanto, você não deve usar tais objetos como chaves em um dicionário / hashtable, como eles são mutáveis. Veja acima.

O tema subjacente aqui é a melhor forma de identificar exclusivamente objetos. Você menciona serialização / desserialização que é importante porque a integridade referencial é perdido nesse processo.

A resposta curta, é que os objetos devem ser identificada exclusivamente pelo menor conjunto de campos imutáveis ??que podem ser usados ??para fazê-lo. Estes são os campos que você deve usar quando overrideing GetHashCode e Equals.

Para testar é perfeitamente razoável para definir o que quer que as afirmações que você precisa, geralmente estes não estão definidos no próprio tipo, mas sim como métodos utilitários no conjunto de testes. Talvez um TestSuite.AssertEquals (MyClass, MyClass)?

Note que GetHashCode e Equals devem trabalhar juntos. GetHashCode deve retornar o mesmo valor para dois objetos se eles são iguais. Equals deve retornar true se e somente se dois objetos têm o mesmo código hash. (Note que é possível que dois objetos podem não ser iguais, mas podem retornar o mesmo código de hash). Há uma abundância de página que resolver este assunto de frente, apenas o Google de distância.

Eu não sei sobre C #, sendo um noob em relação a ele, mas em Java, se você substituir equals () você também precisa substituir hashCode () para manter o contrato entre eles (e vice-versa) .. . e Java também tem o mesmo captura 22; basicamente forçando você usa campos imutáveis ??... Mas esta é uma questão apenas para as classes que são usados ??como uma chave hash e Java tem implementações alternativas para todas as coleções baseadas em hash ... que talvez não tão rápido, mas eles fazem effecitely permitem que você use um objeto mutável como uma chave ... é só (normalmente) franziu a testa como uma "má concepção".

E eu sinto o desejo de salientar que este problema fundamental é atemporal ... Tem sido em torno desde que Adão era um rapaz.

Eu trabalhei em código Fortran que é mais velho que eu (eu sou 36) que quebra quando um nome de usuário é alterado (como quando uma garota se casa, ou divorciado ;-) ... Assim é a engenharia, The solução adoptada era: o "método de" GetHashCode lembra o hashCode previamente calculado, recalcula o hashCode (isto é, um marcador IsDirty virtual) e se as keyfields mudaram ele retorna nulo. Isso faz com que o cache para excluir o usuário "sujo" (chamando outra GetPreviousHashCode) e, em seguida, o nulo de cache retorna, fazendo com que o usuário re-ler a partir do banco de dados. Um interessante e corte vale a pena; mesmo se eu me dizê-lo; -)

Vou trade-off mutabilidade (somente desejável em casos de canto) para O (1) o acesso (desejável em todos os casos). Bem-vindo à engenharia; a terra do compromisso informado.

Saúde. Keith.

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