Já faz muito tempo, mas, no entanto, acho que ainda é necessário dar uma resposta correta a essa pergunta, incluindo explicações sobre os porquês e os comos. A melhor resposta até agora é a que cita exaustivamente o MSDN - não tente criar suas próprias regras, os funcionários da MS sabiam o que estavam fazendo.
Mas primeiro as primeiras coisas: a diretriz citada na pergunta está errada.
Agora os porquês - há dois deles
Primeiro, por que : Se o código de hash for calculado de alguma maneira, ele não será alterado durante a vida útil de um objeto, mesmo que o próprio objeto seja alterado, isso quebraria o contrato de igual.
Lembre-se: "Se dois objetos forem comparados como iguais, o método GetHashCode para cada objeto deverá retornar o mesmo valor. No entanto, se dois objetos não forem comparados como iguais, os métodos GetHashCode para os dois objetos não precisarão retornar valores diferentes."
A segunda frase geralmente é mal interpretada como "A única regra é que, no momento da criação do objeto, o código hash de objetos iguais deve ser igual". Realmente não sei o porquê, mas essa é também a essência da maioria das respostas aqui.
Pense em dois objetos que contêm um nome, onde o nome é usado no método equals: Mesmo nome -> mesma coisa. Criar instância A: Nome = Joe Criar instância B: Nome = Peter
Hashcode A e Hashcode B provavelmente não serão os mesmos. O que aconteceria agora, quando o Nome da instância B for alterado para Joe?
De acordo com a diretriz da pergunta, o código de hash de B não mudaria. O resultado seria: A.Equals (B) ==> true Mas, ao mesmo tempo: A.GetHashCode () == B.GetHashCode () ==> false.
Mas exatamente esse comportamento é proibido explicitamente pelo equals & hashcode-contract.
Segundo por que : Embora seja verdade que as alterações no código hash possam quebrar listas de hash e outros objetos usando o código hash, o inverso também é verdadeiro. Se você não alterar o código de hash, no pior dos casos, obterá listas de hash, onde muitos objetos diferentes terão o mesmo código de hash e, portanto, o mesmo hash bin - acontece quando objetos são inicializados com um valor padrão, por exemplo.
Agora, chegando aos comos Bem, à primeira vista, parece haver uma contradição - de qualquer forma, o código irá quebrar. Mas nenhum problema vem de código hash alterado ou inalterado.
A fonte dos problemas está bem descrita no MSDN:
Na entrada da tabela de hash do MSDN:
Os objetos-chave devem ser imutáveis desde que sejam utilizados como chaves no Hashtable.
Isso significa:
Qualquer objeto que cria um valor hash deve alterar o valor hash, quando o objeto muda, mas não deve - absolutamente não deve - permitir alterações a si próprio, quando é usado dentro de um Hashtable (ou qualquer outro objeto que use Hash, é claro) .
Primeiro, como a maneira mais fácil seria, obviamente, projetar objetos imutáveis apenas para uso em hashtables, que serão criados como cópias dos objetos normais e mutáveis, quando necessário. Dentro dos objetos imutáveis, é óbvio que é bom armazenar em cache o código hash, pois é imutável.
Segundo como: Ou dê ao objeto uma bandeira "você está com hash agora", verifique se todos os dados do objeto são privados, verifique o sinalizador em todas as funções que podem alterar os dados dos objetos e ative uma exceção se a alteração não for permitida (ou seja, o sinalizador está definido ) Agora, quando você colocar o objeto em qualquer área com hash, certifique-se de definir o sinalizador e - também - desative o sinalizador quando ele não for mais necessário. Para facilitar o uso, aconselho definir o sinalizador automaticamente dentro do método "GetHashCode" - desta forma, não pode ser esquecido. E a chamada explícita de um método "ResetHashFlag" garantirá que o programador tenha que pensar se é ou não permitido alterar os dados dos objetos agora.
Ok, o que deve ser dito também: Há casos em que é possível ter objetos com dados mutáveis, em que o código hash permanece inalterado, quando os dados dos objetos são alterados, sem violar os contratos iguais e hashcode.
No entanto, isso exige que o método dos iguais também não seja baseado nos dados mutáveis. Portanto, se eu escrever um objeto e criar um método GetHashCode que calcule um valor apenas uma vez e o armazene dentro do objeto para retorná-lo em chamadas posteriores, devo: novamente: absolutamente preciso, criar um método Equals, que usará valores armazenados para a comparação, para que A.Equals (B) nunca mude de falso para verdadeiro também. Caso contrário, o contrato seria quebrado. O resultado disso geralmente será que o método Equals não faz sentido - não é a referência original igual, mas também não é um valor igual. Às vezes, esse pode ser o comportamento pretendido (ou seja, registros de clientes), mas geralmente não é.
Portanto, basta alterar o resultado de GetHashCode, quando os dados do objeto forem alterados e se o uso do objeto dentro de hash usando listas ou objetos for pretendido (ou apenas possível), tornar o objeto imutável ou criar um sinalizador somente leitura para usar no vida útil de uma lista de hash que contém o objeto.
(A propósito: Tudo isso não é C # ou específico do .NET - é da natureza de todas as implementações de hashtable, ou mais geralmente de qualquer lista indexada, que os dados de identificação dos objetos nunca devem mudar, enquanto o objeto está na lista Comportamento inesperado e imprevisível ocorrerá, se essa regra for violada. Em algum lugar, pode haver implementações de lista que monitoram todos os elementos da lista e reindexam a lista automaticamente - mas o desempenho desses certamente será horrível, na melhor das hipóteses.