2008-09-09 8 views
14

¿Se garantiza que el valor de retorno de GetHashCode() será consistente suponiendo que se utiliza el mismo valor de cadena? (C#/ASP.NET)¿Puedo depender de los valores de GetHashCode() para ser consistente?

He subido mi código a un servidor hoy y para mi sorpresa tuve que reindexar algunos datos porque mi servidor (win2008 de 64 bits) devolvía valores diferentes en comparación con mi computadora de escritorio.

Respuesta

29

Si no me equivoco, GetHashCode es coherente dado el mismo valor, pero NO se garantiza que sea coherente en las diferentes versiones del marco.

De la documentación de MSDN en String.GetHashCode():

El comportamiento de GetHashCode depende de su aplicación, lo que podría cambiar de una versión de tiempo de ejecución de lenguaje común a otro. Una razón por la que esto podría suceder es mejorar el rendimiento de GetHashCode.

+1

Conclusión: Nunca persista ni transmita el resultado de 'GetHashCode()'. Úselo solo para su propósito previsto: facilitar el uso de tablas hash. –

0

Me pregunto si hay diferencias entre los sistemas operativos de 32 bits y 64 bits, porque estoy seguro de que tanto mi servidor y ordenador de casa se están ejecutando la misma versión de .NET

Siempre estaba cansada de usando GetHashCode(), podría ser una buena idea que simplemente represente mi propio algoritmo hash. Bueno, al menos terminé escribiendo una página rápida de re-indexar .aspx por eso.

0

¿Está ejecutando Win2008 x86 como su escritorio? Porque Win2008 incluye la versión 2.0.50727.1434, que es una versión actualizada de 2.0 incluida en Vista RTM.

0

No es una respuesta directa a su pregunta, que Jonas ha respondido bien, sin embargo esto puede ser de ayuda si usted está preocupado acerca de las pruebas de igualdad en los hashes

De nuestras pruebas, dependiendo de lo que está requiriendo con hashcodes, en C#, los códigos hash no necesitan ser únicos para las operaciones de Igualdad. Como ejemplo, considere lo siguiente:

Teníamos el requisito de sobrecargar el operador igual, y por lo tanto la función GetHashCode de nuestros objetos ya que se habían vuelto volátiles y sin estado, y se abastecían directamente de datos, por lo que en un lugar de la aplicación que necesitábamos para garantizar que un objeto se viera como igual a otro objeto si se obtuviera de los mismos datos, no solo si era la misma referencia. Nuestros identificadores de datos únicos son Guids.

El operador de iguales fue fácil de atender, ya que acabamos de comprobar en el Guid del registro (después de comprobar nulo).

Desafortunadamente, el tamaño de los datos HashCode (ser un int) depende del sistema operativo, y en nuestro sistema de 32 bits, el código hash sería de 32 bits. Matemáticamente, cuando anulamos la función GetHashCode, es imposible generar un hashcode exclusivo a partir de un guid que sea mayor que 32 bit (míralo desde el inverso, ¿cómo traducirías un entero de 32 bits a un guid?).

Luego realizamos algunas pruebas donde tomamos el Guid como una cadena y devolvimos el HashCode del Guid, que casi siempre devuelve un identificador único en nuestras pruebas, pero no siempre.

Lo que notamos sin embargo, cuando un objeto está en un objeto de recolección hash (una tabla hash, un diccionario, etc.), cuando 2 objetos no son únicos sino sus hashcodes, el hashcode solo se usa como primera opción de búsqueda. si se utilizan códigos hash no exclusivos, el operador de igualdad siempre se utiliza como un retroceso para desviar la igualdad.

Como dije, esto puede o no ser relevante para su situación, pero si lo es, es un consejo útil.

ACTUALIZACIÓN

Para demostrar, tenemos una Hashtable:

Clave: Objeto A (Hashcode 1), del valor del objeto A1

Key: Objeto B (Hashcode 1), el valor de objetos B1

Key: objeto C (Hashcode 1), del valor del objeto C1

Key: objeto D (Hashcode 2), el valor Ob Ject D1

Key: Objeto E (Hashcode 3), de objetos de valor E1

Cuando llamo la tabla hash para el objeto con la clave de objeto A, se devolverá el objeto A1 después de 2 pasos, una petición de hashcode 1, luego una comprobación de igualdad en el objeto clave ya que no hay una clave única con el código hash 1

Cuando llamo a la tabla hash del objeto con la clave del objeto D, el objeto D1 se devolverá después de 1 paso , una búsqueda hash

0

Lo que sí notamos sin embargo, cuando un objeto está en un objeto de colección de hash (una tabla hash, un diccionario etc), cuando 2 objetos no son únicos pero sus hashcodes son, el código hash sólo se utiliza como una primera búsqueda opción, si hay no único códigos de hash en uso, el operador de igualdad es siempre utilizado como retroceso a detirmine igualdad.

Esta es la forma en que funcionan las búsquedas de hash, ¿no? Cada segmento contiene una lista de elementos que tienen el mismo código hash.

Para encontrar el elemento correcto en estas condiciones, se lleva a cabo una búsqueda lineal utilizando la comparación de igualdad de valores.

Y si su implementación hash logra una buena distribución, esta búsqueda no es necesaria, es decir, un elemento por segmento.

¿Es correcto mi entendimiento?

+0

Ben, según nuestra prueba, esto es cierto. La segunda búsqueda de igualdad solo se ejecuta según sea necesario. Puede probarlo usted mismo al sobrecargar ==,! =, Equals() y GetHashCode de un determinado objeto. Lo encontré muy interesante (pero soy geek :)) – johnc

+0

(continuación), por lo que el efecto knock-on de los códigos hash no únicos sería un rendimiento más lento para ejecutar la verificación de igualdad, pero en nuestra situación donde el valor no único es muy raro, es en gran medida insignificante – johnc

5

La implementación depende de la versión del marco, pero también depende de architecture. La implementación de string.GetHashCode() es diferente en las versiones x86 y x64 del marco, incluso si tienen el mismo número de versión.

10

Tuve un problema similar cuando llené una tabla de base de datos con información que dependía de String.GetHashCode (No era la mejor idea) y cuando actualicé el servidor en el que estaba trabajando x64 noté los valores que obtenía de Cuerda.GetHashCode no concuerda con lo que ya estaba en la mesa. Mi solución fue usar mi propia versión de GetHashCode, que devuelve el mismo valor que String.GetHashCode en un marco x86.

Aquí está el código, no se olvide de compilar con "Permitir código no seguro":

/// <summary> 
    /// Similar to String.GetHashCode but returns the same as the x86 version of String.GetHashCode for x64 and x86 frameworks. 
    /// </summary> 
    /// <param name="s"></param> 
    /// <returns></returns> 
    public static unsafe int GetHashCode32(string s) 
    { 
     fixed (char* str = s.ToCharArray()) 
     { 
      char* chPtr = str; 
      int num = 0x15051505; 
      int num2 = num; 
      int* numPtr = (int*)chPtr; 
      for (int i = s.Length; i > 0; i -= 4) 
      { 
       num = (((num << 5) + num) + (num >> 0x1b))^numPtr[0]; 
       if (i <= 2) 
       { 
        break; 
       } 
       num2 = (((num2 << 5) + num2) + (num2 >> 0x1b))^numPtr[1]; 
       numPtr += 2; 
      } 
      return (num + (num2 * 0x5d588b65)); 
     } 
    } 
+1

Tuve el mismo problema y porté tu versión a un método seguro. https://gist.github.com/gerriten/7542231#file-gethashcode32-net –

-1

Yo tendría que decir ... no se puede confiar en él. Por ejemplo, si ejecuto el archivo 1 a través del código hash md5 de C# y copio nd y pego el mismo archivo en un nuevo directorio ... el código hash sale diferente aunque sea el mismo archivo. Obviamente es la misma versión .net, mismo todo. Lo único que cambió fue el camino.

1
/// <summary> 
    /// Default implementation of string.GetHashCode is not consistent on different platforms (x32/x64 which is our case) and frameworks. 
    /// FNV-1a - (Fowler/Noll/Vo) is a fast, consistent, non-cryptographic hash algorithm with good dispersion. (see http://isthe.com/chongo/tech/comp/fnv/#FNV-1a) 
    /// </summary> 
    private static int GetFNV1aHashCode(string str) 
    { 
     if (str == null) 
      return 0; 
     var length = str.Length; 
     // original FNV-1a has 32 bit offset_basis = 2166136261 but length gives a bit better dispersion (2%) for our case where all the strings are equal length, for example: "3EC0FFFF01ECD9C4001B01E2A707" 
     int hash = length; 
     for (int i = 0; i != length; ++i) 
      hash = (hash^str[i]) * 16777619; 
     return hash; 
    } 

Esta implementación puede ser más lenta que la insegura publicada anteriormente. Pero mucho más simple y seguro.

Cuestiones relacionadas