Pergunta

Eu tenho várias classes que, por determinadas razões, não seguem o contrato oficial Equals. No GetHashCode() substituído essas classes simplesmente retornar 0 para que eles possam ser usados ??em um HashMap.

Algumas destas classes implementam a mesma interface e há HashMaps usando essa interface como chave. Então eu percebi que cada classe deve, pelo menos, devolver um valor diferente (mas ainda constante) em GetHashCode().

A questão é como selecionar este valor. Devo simplesmente deixar a primeira classe de retorno 1, a próxima classe 2 e assim por diante? Ou devo tentar algo como

class SomeClass : SomeInterface {
    public overwrite int GetHashCode() {
        return "SomeClass".GetHashCode();
    }
}

para que o hash é distribuído mais uniformemente? (Eu tenho que armazenar em cache o valor retornado mim ou é compilador da Microsoft capaz de otimizar isso?)

Update: Não é possível retornar um hashcode individual para cada objeto, porque Equals viola o contrato. Specifially, estou referindo-se a este problema .

Foi útil?

Solução

Eu corri para este problema exato ao escrever uma classe vector. Eu queria comparar vetores para a igualdade, mas as operações flutuador dar erros de arredondamento, então eu queria a igualdade aproximada. Para encurtar a história, substituindo iguais é uma má idéia a menos que sua implementação é simétrica, reflexiva e transitiva.

Outras classes vão assumir iguais tem essas propriedades, e assim vai classes usando essas classes, e assim você pode acabar em casos estranhos. Por exemplo, uma lista pode impor exclusividade, mas acabar com dois elementos que avaliam como igual a algum elemento B.

Uma tabela hash é o exemplo perfeito de um comportamento imprevisível quando você quebra a igualdade. Por exemplo:

//Assume a == b, b == c, but a != c
var T = new Dictionary<YourType, int>()
T[a] = 0
T[c] = 1
return T[b] //0 or 1? who knows!

Outro exemplo seria um Set:

//Assume a == b, b == c, but a != c
var T = new HashSet<YourType>()
T.Add(a)
T.Add(c)
if (T.contains(b)) then T.remove(b)
//surely T can't contain b anymore! I sure hope no one breaks the properties of equality!
if (T.contains(b)) then throw new Exception()

Eu sugiro usar um outro método, com um nome como ApproxEquals. Você também pode considerar substituindo o operador ==, porque não é virtual e, portanto, não será usado acidentalmente por outras classes como iguais poderia ser.

Se você realmente não pode usar a igualdade de referência para a tabela hash, não arruinar o desempenho de casos em que você pode. Adicionar uma interface IApproxEquals, implementá-lo em sua classe, e adicionar um método de extensão GetApprox ao dicionário que enumera as chaves que procuram um um aproximadamente igual, e retorna o valor associado. Você também pode escrever um dicionário personalizado especialmente para vetores em 3 dimensões, ou o que você precisa.

Outras dicas

Se "viola o contrato é igual a", então eu não estou certo que você deve usá-lo como uma chave.

É algo está usando isso como uma chave, você realmente precisa para obter o direito de hashing ... é muito claro o que a lógica Equals é, mas dois valores que são considerados iguais deve tem o mesmo hash-código. Não é necessário que dois valores com o mesmo hash-code são iguais.

Usando uma string constante não vai realmente ajuda muito - você vai obter os valores divididos igualmente sobre os tipos, mas que é sobre ele ...

Estou curioso que o raciocínio seria para substituir GetHashCode() e retornar um valor constante. Por violar a ideia de um hash em vez de apenas violar o "contrato" e não substituindo a função GetHashCode() em tudo e deixar a implementação padrão de Object?

Editar

Se o que você fez é que assim que você pode ter seus objetos correspondem com base em seu conteúdo, em vez da sua referência, em seguida, o que se propõe a ter diferentes classes simplesmente usar diferentes constantes podem trabalhar, mas é altamente ineficiente. O que você quer fazer é chegar a um algoritmo de hash que pode levar o conteúdo de sua classe e produzir um valor que equilibra velocidade com distribuição uniforme (que de hashing 101).

Eu acho que eu não sei o que você está procurando ... não há um "bom" esquema para escolher os números constantes para este paradigma. Um não é melhor que o outro. Tente melhorar seus objetos de modo que você está criando um hash real.

Quando ocorrem colisões de hash, o HashTable / dicionário chama Igual para encontrar a chave que você está procurando. Usando um código de hash constante remove as vantagens de velocidade de usar um hash em primeiro lugar -. Torna-se uma busca linear

Você está dizendo que o método Equals não foi implementado de acordo com o contrato. O que exatamente você quer dizer com isso? Dependendo do tipo de violação, o HashTable ou dicionário vai apenas ser lenta (pesquisa linear) ou não funcionar em todos.

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