2009-06-05 9 views
13

que tienen una clase de direcciones en C# que tiene este aspecto:¿Debo usar una concatenación de mis campos de cadena como un código hash?

public class Address 
{    
    public string StreetAddress { get; set; } 
    public string RuralRoute { get; set; } 
    public string City { get; set; } 
    public string Province { get; set; } 
    public string Country { get; set; } 
    public string PostalCode { get; set; } 
} 

estoy poniendo en práctica la igualdad y por eso es necesario anular el código hash. Al principio iba a usar la fórmula de hashcode de EJ pero luego pensé: estos son todos los campos de cadenas, ¿no puedo simplemente usar un StringBuilder para concatenarlos y devolver el código de hash de esa cadena?

Es decir:

var str = new StringBuilder(); 
str.Append(StreetAddress) 
    .Append(RuralRoute) 
    ... 

return str.ToString().GetHashCode(); 

¿Cuáles son las ventajas/desventajas de esto? ¿Por qué no debería hacerlo?

+1

Para su consideración: no http://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-systemobjectgethashcode – lance

Respuesta

14

Evitaría hacer eso simplemente con el argumento de que crea un montón de cadenas sin sentido, aunque el punto de Kosi2801 acerca de hacer las colisiones simples también es relevante. (Sospecho que no lo sería en realidad crear muchas colisiones, debido a la naturaleza de los campos, pero ...)

Yo iría por el "simple y fácil de hacerlo bien" algoritmo he previamente used in this answer (gracias por buscarlo lanza :) - y que se enumera en Java efectivo, como dijiste. En este caso, terminaría como:

public int GetHashCode() 
{ 
    int hash = 17; 
    // Suitable nullity checks etc, of course :) 
    hash = hash * 23 + StreetAddress.GetHashCode(); 
    hash = hash * 23 + RuralRoute.GetHashCode(); 
    hash = hash * 23 + City.GetHashCode(); 
    hash = hash * 23 + Province.GetHashCode(); 
    hash = hash * 23 + Country.GetHashCode(); 
    hash = hash * 23 + PostalCode.GetHashCode(); 
    return hash; 
} 

Eso no es nulo seguro, por supuesto. Si estás usando C# 3 es posible que desee considerar un método de extensión:

public static int GetNullSafeHashCode<T>(this T value) where T : class 
{ 
    return value == null ? 1 : value.GetHashCode(); 
} 

continuación, puede utilizar:

public int GetHashCode() 
{ 
    int hash = 17; 
    // Suitable nullity checks etc, of course :) 
    hash = hash * 23 + StreetAddress.GetNullSafeHashCode(); 
    hash = hash * 23 + RuralRoute.GetNullSafeHashCode(); 
    hash = hash * 23 + City.GetNullSafeHashCode(); 
    hash = hash * 23 + Province.GetNullSafeHashCode(); 
    hash = hash * 23 + Country.GetNullSafeHashCode(); 
    hash = hash * 23 + PostalCode.GetNullSafeHashCode(); 
    return hash; 
} 

Usted podría crear una utilidad parámetro del método de matriz para hacer esto aún más sencillo :

public static int GetHashCode(params object[] values) 
{ 
    int hash = 17; 
    foreach (object value in values) 
    { 
     hash = hash * 23 + value.GetNullSafeHashCode(); 
    } 
    return hash; 
} 

y llamarlo con:

public int GetHashCode() 
{ 
    return HashHelpers.GetHashCode(StreetAddress, RuralRoute, City, 
            Province, Country, PostalCode); 
} 

En la mayoría de los tipos hay primitivas involucradas, por lo que realizaría el boxeo de forma innecesaria, pero en este caso solo tendría referencias. Por supuesto, que terminarías creación de una matriz de forma innecesaria, pero ya sabes lo que dicen acerca de la optimización prematura ...

+1

Otra solución es utilizar EqualityComparer . Default.GetHashCode (someValue). Este es un mecanismo de hash seguro nulo y está en el marco desde 2.0 – JaredPar

+0

Gracias, eso funciona perfecto. Hay un pequeño error en su método GetNullSafeHashCode(): le falta el "esto". – cdmckay

+0

¡Doh! Muchas gracias. Fijo. –

5

No hagas eso porque los objetos pueden ser diferentes aunque el hashcode sea el mismo.

Piense en

"StreetAddress" + "RuralRoute" + "City" 

vs

"Street" + "AddressRural" + "RouteCity" 

Ambos tendrán el mismo código hash, pero diferente contenido en los campos.

+0

Ese es un buen punto, tuve incluso considerado eso. Aunque parece poco probable que suceda en la práctica. – cdmckay

+1

¿No se puede resolver fácilmente colocando un delimitador entre los campos? (Por ejemplo '" StreetAddress "+"/delimiter/"+" RuralRoute "+"/delimiter/"+" City "'. ¿Podría ser una variable para evitar asignaciones múltiples en algunos idiomas?) – simonra

0

Para este tipo de cosas, es posible que desee aplicar IEqualityComparer<Address>:

public class Address : IEqualityComparer<Address> 
{   
    // 
    // member declarations 
    // 

    bool IEqualityComparer<Address>.Equals(Address x, Address y) 
    { 
     // implementation here 
    } 

    int IEqualityComparer<Address>.GetHashCode(Item obj) 
    { 
     // implementation here 
    } 
} 

Usted también podría implementar IComparable<Address> para ordenar ...

-4
public string getfourDigitEncryptedText(string input) { 
    int hashCode = input.hashCode(); 
    string hstring = (new StringBuilder()).append(hashCode).append("").toString(); 
    string rev_hstring = (new StringBuilder(hstring)).reverse().toString(); 
    string parts[] = rev_hstring.trim().split(""); 
    int prefixint = 0; 
    for(int i = 1; i <= parts.length - 3; i++) 
     prefixint += integer.parseInt(parts[i]); 
    string prefixstr = "0"; 
    if((new integer(prefixint)).toString().length() < 2) 
     prefixstr = (new StringBuilder()).append((new integer(prefixint)).toString()).append("5").toString(); 
    else if((new integer(prefixint)).toString().length() > 2) 
     prefixstr = "79"; 
    else 
     prefixstr = (new integer(prefixint)).toString(); 
    string finalstr = (new StringBuilder()).append(prefixint).append(rev_hstring.substring(3, 5)).toString(); 
    return finalstr; 
} 
+0

¿Puede proporcionar una explicación de lo que hace su código? – DeanOC

Cuestiones relacionadas