Se você usar o próprio menu de refatoração do Visual Studio para adicionar uma implementação GetHashCode a uma classe como esta:
e selecione a única propriedade int na classe:
ele gera esse código no .NET Framework:
public override int GetHashCode()
{
return -1937169414 + Value.GetHashCode();
}
(gera HashCode.Combine(Value)
no .NET Core, o que não tenho certeza se envolve o mesmo valor)
O que há de especial nesse valor? Por que o Visual Studio não usa Value.GetHashCode()
diretamente? Pelo que entendi, isso realmente não afeta a distribuição de hash. Como é apenas uma adição, valores consecutivos ainda se acumulam juntos.
Edição: Eu só tentei isso com diferentes classes com Value
propriedades, mas aparentemente o nome da propriedade afeta o número gerado. Por exemplo, se você renomear a propriedade como Halue
, o número se tornará 387336856. Agradecimentos a Gökhan Kurt, que apontou isso.
fonte
int
.-1937169414
é a multiplicação inteira de-1521134295
e-783812246
. O número mais significativo aqui é o-1521134295
que aparece em todo cálculo de código de hash.-783812246
é o número da semente. Um número de semente é escolhido com base no número de membros na equação. Em classes anônimas, o número da semente é calculado com base nos nomes dos campos. Portanto, existem tantos números de sementes quanto números inteiros. Podemos assumir que um número inicial é aleatório. Quanto ao significado de-1521134295
, acho que reduz a colisão e apenas um desenvolvedor interno seria capaz de responder com precisão como.Respostas:
Se você procurar
-1521134295
nos repositórios da Microsoft, verá que ele aparece várias vezesA maioria dos resultados da pesquisa está nas
GetHashCode
funções, mas todas elas têm o seguinte formatoO primeiro
hashCode * -1521134295 = SOME_CONSTANT * -1521134295
será pré-multiplicado durante o tempo de geração pelo gerador ou durante o tempo de compilação pelo CSC. Essa é a razão para-1937169414
no seu códigoAprofundar nos resultados revela a parte da geração de código que pode ser encontrada na função CreateGetHashCodeMethodStatements
Como você pode ver, o hash depende dos nomes dos símbolos. Nessa função, a constante também é chamada
permuteValue
, provavelmente porque após a multiplicação os bits são permutados de alguma maneiraExistem alguns padrões se visualizarmos o valor em binário:
101001 010101010101010 101001 01001
ou10100 1010101010101010 10100 10100 1
. Mas se multiplicarmos um valor arbitrário por isso, haverá muitas cargas sobrepostas, então não pude ver como funciona. A saída também pode ter um número diferente de bits definidos, portanto, não é realmente uma permutaçãoVocê pode encontrar outro gerador no AnonymousTypeGetHashCodeMethodSymbol de Roslyn, que chama a constante
HASH_FACTOR
A verdadeira razão para escolher esse valor ainda não está clara
fonte
Como GökhanKurt explicou nos comentários, o número muda com base nos nomes das propriedades envolvidas. Se você renomear a propriedade como
Halue
, o número se tornará 387336856. Eu tentei com classes diferentes, mas não pensei em renomear a propriedade.O comentário de Gökhan me fez entender seu propósito. Ele está compensando valores de hash com base em um deslocamento determinístico, mas distribuído aleatoriamente. Dessa forma, a combinação de valores de hash para diferentes classes, mesmo com uma simples adição, ainda é um pouco resistente a colisões de hash.
Por exemplo, se você tiver duas classes com implementações GetHashCode semelhantes:
e se você tiver outra classe que contenha referências a esses dois:
uma combinação ruim como essa seria propensa a colisões de hash, porque o código de hash resultante se acumularia na mesma área para valores diferentes de ValueA e ValueB se os valores deles estivessem próximos. Realmente não importa se você usa operações de multiplicação ou bit a bit para combiná-las, elas ainda estarão sujeitas a colisões sem um deslocamento uniformemente distanciado. Como muitos valores inteiros usados na programação são acumulados em torno de 0, faz sentido usar esse deslocamento
Aparentemente, é uma boa prática ter um deslocamento aleatório com bons padrões de bits.
Ainda não sei por que eles não usam deslocamentos completamente aleatórios, provavelmente para não quebrar nenhum código que dependa do determinismo de GetHashCode (), mas seria ótimo receber um comentário da equipe do Visual Studio sobre isso.
fonte