2009-06-05 11 views
13

Ich habe eine Adresse Klasse in C#, die wie folgt aussieht:Sollte ich eine Verkettung meiner String-Felder als Hash-Code verwenden?

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; } 
} 

Ich bin Gleichheit Implementierung und so muss ich den Hash-Code zu überschreiben. Zuerst wollte ich die Hashcode-Formel von EJ verwenden, aber dann dachte ich: Das sind alles String-Felder, kann ich nicht einfach einen StringBuilder verwenden, um sie zu verketten und den Hash-Code aus dieser Zeichenfolge zurückzugeben?

Das heißt:

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

return str.ToString().GetHashCode(); 

Was sind die Vorteile/Nachteile davon? Warum sollte ich das nicht tun?

+1

Für Ihre Überlegung: http://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-systemobjectgethashcode – lance

Antwort

14

ich vermeiden würde tun, dass einfach der Begründung, dass es eine Reihe von Strings pointlessly schafft - obwohl Kosi2801 den Punkt zu machen Kollisionen einfach auch relevant ist. (Ich vermute, wäre es nicht tatsächlich viele Kollisionen erzeugen, aufgrund der Art der Felder, aber ...)

Ich würde für die gehen Algorithmus „einfach und leicht richtig zu machen“ habe ich vorher used in this answer (Danke, dass du nach Lance geschaut hast) - und was in Effective Java steht, wie du gesagt hast. In diesem Fall würde es enden als:

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; 
} 

Das ist natürlich nicht null-sicher. Wenn Sie C# 3 verwenden möchten Sie vielleicht eine Erweiterungsmethode berücksichtigen:

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

Dann können Sie verwenden:

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; 
} 

Sie könnte eine Parameter-Array-Methode Dienstprogramm erstellen, um diese noch einfacher :

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

und nennen sie es mit:

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

In den meisten Typen sind Primitive beteiligt, so dass das Boxen etwas unnötigerweise ausgeführt würde, aber in diesem Fall hätten Sie nur Referenzen. Natürlich würden Sie unnötig ein Array am Ende die Schaffung, aber wissen Sie, was sie über vorzeitige Optimierung sagen ...

+1

Eine andere Lösung ist EqualityComparer . Default.GetHashCode (someValue). Dies ist ein Null-Safe-Hashing-Mechanismus und ist im Framework seit 2.0 – JaredPar

+0

Danke, das funktioniert perfekt. Es gibt einen kleinen Fehler in der GetNullSafeHashCode() -Methode: Es fehlt das "This". – cdmckay

+0

Doh! Vielen Dank. Fest. –

5

Tun Sie das nicht, weil die Objekte unterschiedlich sein können altough die Hash-Code gleich ist.

Denken Sie an

"StreetAddress" + "RuralRoute" + "City" 

vs

"Street" + "AddressRural" + "RouteCity" 

Beide werden den gleichen Hash-Code, aber unterschiedliche Inhalte in den Bereichen haben.

+0

Dass ein guter Punkt ist, ich habe nicht einmal dachte das. Obwohl es in der Praxis unwahrscheinlich scheint. – cdmckay

+1

Kann man das nicht einfach lösen, indem man ein Trennzeichen zwischen die Felder legt? (ZB "StreetAddress" + "/ delimiter /" + "RuralRoute" + "/ delimiter /" + "City" '. Kann eine Variable sein, um Mehrfachzuweisungen in einigen Sprachen zu vermeiden?) – simonra

0

Für diese Art der Sache, möchten Sie vielleicht implementieren 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 
    } 
} 

Sie könnte auch implementieren IComparable<Address> um zu bestellen ...

-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

Können Sie erläutern, was Ihr Code macht? – DeanOC