Saltar al contenido

¿Por qué Visual Studio agrega "-1937169414" a un cálculo de código hash generado?

Agradecemos tu apoyo para compartir nuestras reseñas en referencia a las ciencias de la computación.

Solución:

Como explicó GökhanKurt en los comentarios, el número cambia según los nombres de propiedad involucrados. Si cambia el nombre de la propiedad a Halue, el número se convierte en 387336856 en su lugar. Lo había probado con diferentes clases pero no pensé en cambiar el nombre de la propiedad.

El comentario de Gökhan me hizo comprender su propósito. Está compensando los valores hash basados ​​en un desplazamiento determinista, pero distribuido aleatoriamente. De esta manera, la combinación de valores hash para diferentes clases, incluso con una simple adición, sigue siendo ligeramente resistente a las colisiones hash.

Por ejemplo, si tiene dos clases con implementaciones similares de GetHashCode:

public class A

    public int Value  get; set;
    public int GetHashCode() => Value;


public class B

    public int Value  get; set;
    public override int GetHashCode() => Value;

y si tiene otra clase que contiene referencias a estos dos:

public class C

    public A ValueA  get; set; 
    public B ValueB  get; set; 
    public override int GetHashCode()
    
        return ValueA.GetHashCode() + ValueB.GetHashCode();
    

una combinación pobre como esta sería propensa a colisiones hash porque el código hash resultante se acumularía alrededor de la misma área para diferentes valores de ValueA y ValueB si sus valores están cerca uno del otro. Realmente no importa si usa operaciones de multiplicación o bit a bit para combinarlos, aún serían propensos a colisiones sin un desplazamiento uniformemente distanciado. Como muchos valores enteros usados ​​en programación se acumulan alrededor de 0, tiene sentido usar tal compensación

Aparentemente, es una buena práctica tener un desplazamiento aleatorio con buenos patrones de bits.

Todavía no estoy seguro de por qué no usan compensaciones completamente aleatorias, probablemente para no romper ningún código que se base en el determinismo de GetHashCode (), pero sería genial recibir un comentario del equipo de Visual Studio sobre esto.

Si buscas -1521134295 en los repositorios de Microsoft, verá que aparece varias veces

  • https://github.com/search?q=org%3Amicrosoft+%22-1521134295%22+OR+0xa5555529&type=Code
  • https://github.com/search?q=org%3Adotnet++%22-1521134295%22+OR+0xa5555529&type=Code

La mayoría de los resultados de la búsqueda se encuentran en la GetHashCode funciones, pero todas tienen la siguiente forma

int hashCode = SOME_CONSTANT;
hashCode = hashCode * -1521134295 + field1.GetHashCode();
hashCode = hashCode * -1521134295 + field2.GetHashCode();
// ...
return hashCode;

La primera hashCode * -1521134295 = SOME_CONSTANT * -1521134295 será pre-multiplicado durante el tiempo de generación por el generador o durante el tiempo de compilación por CSC. Esa es la razón de -1937169414 en tu código

Profundizar en los resultados revela la parte de generación de código que se puede encontrar en la función CreateGetHashCodeMethodStatements

const int hashFactor = -1521134295;

var initHash = 0;
var baseHashCode = GetBaseGetHashCodeMethod(containingType);
if (baseHashCode != null)

    initHash = initHash * hashFactor + Hash.GetFNVHashCode(baseHashCode.Name);


foreach (var symbol in members)

    initHash = initHash * hashFactor + Hash.GetFNVHashCode(symbol.Name);

Como puede ver, el hash depende de los nombres de los símbolos. En esa función, la constante también se llama permuteValue, probablemente porque después de la multiplicación los bits se permutan de alguna manera

// -1521134295
var permuteValue = CreateLiteralExpression(factory, hashFactor);

Hay algunos patrones si vemos el valor en binario: 101001 010101010101010 101001 01001 o 10100 1010101010101010 10100 10100 1. Pero si multiplicamos un valor arbitrario con eso, entonces hay muchos acarreos superpuestos, por lo que no pude ver cómo funciona. La salida también puede tener un número diferente de bits establecidos, por lo que no es realmente una permutación

Puede encontrar otro generador en AnonymousTypeGetHashCodeMethodSymbol de Roslyn que llama a la constante HASH_FACTOR

//  Method body:
//
//  HASH_FACTOR = 0xa5555529;
//  INIT_HASH = (...((0 * HASH_FACTOR) + GetFNVHashCode(backingFld_1.Name)) * HASH_FACTOR
//                                     + GetFNVHashCode(backingFld_2.Name)) * HASH_FACTOR
//                                     + ...
//                                     + GetFNVHashCode(backingFld_N.Name)

La verdadera razón para elegir ese valor aún no está clara.

valoraciones y comentarios

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada.