Wie Sie GetHashCode für Struktur mit zwei String implementieren, wenn beide Strings untereinander austauschbar sind

StackOverflow https://stackoverflow.com/questions/70303

  •  09-06-2019
  •  | 
  •  

Frage

Ich habe eine Struktur in C #:

public struct UserInfo
{
   public string str1
   {
     get;
     set;
   }

   public string str2
   {
     get;
     set;
   }   
}

Die einzige Regel ist, dass UserInfo(str1="AA", str2="BB").Equals(UserInfo(str1="BB", str2="AA"))

Wie die GetHashCode-Funktion für diese Struktur außer Kraft zu setzen?

War es hilfreich?

Lösung

MSDN :

Eine Hash-Funktion muss folgende Eigenschaften haben:

  
      
  • Wenn zwei Objekte gleich, ist für jedes Objekt die GetHashCode Methode muss den gleichen Wert zurück. Wenn jedoch zwei Objekte vergleichen, nicht als gleich, die GetHashCode Methoden für die beiden Objekte nicht unterschiedliche Werte zurückgeben müssen.
  •   
  • Die GetHashCode Methode für ein Objekt müssen den gleichen Hash-Code so lange konsequent zurück, da es keine Änderung an dem Objekt-Zustand befindet, der den Rückgabewert des Equals Methode des Objekts bestimmt. Beachten Sie, dass dies nur für die aktuelle Ausführung einer Anwendung wahr ist, und dass ein anderer Hash-Code kann zurückgegeben werden, wenn die Anwendung erneut ausgeführt wird.
  •   
  • Für die beste Leistung muss eine Hash-Funktion eine zufällige Verteilung für alle Eingaben erzeugen.
  •   

es zu berücksichtigen richtige Art und Weise zu nehmen ist:

return str1.GetHashCode() ^ str2.GetHashCode() 

^ kann mit anderen kommutative Operation ersetzt werden

Andere Tipps

Jon Skeet Antwort Siehe - Binäroperationen wie ^ nicht gut sind, werden sie oft erzeugen Hash-Kollision

public override int GetHashCode()
{
    unchecked
    {
        return (str1 ?? String.Empty).GetHashCode() +
            (str2 ?? String.Empty).GetHashCode();
    }
}

den ‚+‘ Operator könnte besser sein, als mit ‚^‘, denn obwohl Sie explizit wollen ( ‚AA‘, BB ') und ( ‚BB‘, ‚AA‘) ausdrücklich das gleiche sein, können Sie will ( 'AA', 'AA') nicht und ( 'BB', 'BB') gleich sein (oder alle gleichen Paare für diese Angelegenheit).

Die ‚so schnell wie möglich‘ Regel ganz nicht, weil im Fall von Nullen in dieser Lösung eingehalten dies eine führt ‚GetHashCode ()‘ auf der leeren Zeichenfolge anstatt sofort eine bekannte Konstante zurück, aber auch ohne explizite Messung ich bin bereit, eine Vermutung zu wagen, dass der Unterschied nicht groß genug wäre, um sich Sorgen zu machen, wenn Sie eine Menge Nullen erwartet.

  1. Als allgemeine Regel gilt, auf einfache Weise einen Hash-Code für eine Klasse zu generieren ist es, alle Datenfelder XOR, die bei der Erzeugung des Hash-Code (wobei darauf geachtet, für null zu überprüfen, wie erwähnt von anderen) teilnehmen können. Dies trifft auch die (künstlich?) Anforderung, dass die Hashcodes für Userinfo ( "AA", "BB") und Userinfo ( "BB", "AA") sind die gleichen.

  2. Wenn Sie Annahmen über die Verwendung Ihrer Klasse machen können, können Sie vielleicht Ihre Hash-Funktion verbessern. Zum Beispiel, wenn es üblich ist für str1 und str2 gleich zu sein, XOR kann nicht eine gute Wahl sein. Aber wenn str1 und str2 darstellen, sagen wir, Vor- und Nachname, XOR ist wahrscheinlich eine gute Wahl.

Obwohl dies eindeutig nicht dazu gedacht, ein reales Beispiel zu sein, kann es sich lohnen, darauf hingewiesen, dass: - Dies ist wahrscheinlich ein schlechtes Beispiel für die Verwendung einer Struktur: Eine Struktur normalerweise Semantik Wert soll, das scheint hier nicht der Fall zu sein. - Verwenden von Eigenschaften mit Setter ein Hash-Code zu generieren, auch Ärger bringen wird.

Ein einfaches allgemeine Art und Weise, dies zu tun:

return string.Format("{0}/{1}", str1, str2).GetHashCode();

Wenn Sie strengen Leistungsanforderungen haben, ist dies die einfachste kann ich mich vorstellen, und ich häufig diese Methode verwenden, wenn ich einen zusammengesetzten Schlüssel benötigen. Es behandelt die null Fällen ganz gut und wird nicht dazu führen (m) keine Hash-Kollisionen (im Allgemeinen). Wenn Sie ‚/‘ in die Saiten erwarten, wählen Sie einfach einen anderen Separator, die Sie nicht erwarten.

public override int GetHashCode()   
{       
    unchecked      
    {           
        return(str1 != null ? str1.GetHashCode() : 0) ^ (str2 != null ? str2.GetHashCode() : 0);       
    }   
}

Gehen entlang der Linien ReSharper ist darauf hindeutet:

public int GetHashCode()
{
    unchecked
    {
        int hashCode;

        // String properties
        hashCode = (hashCode * 397) ^ (str1!= null ? str1.GetHashCode() : 0);
        hashCode = (hashCode * 397) ^ (str2!= null ? str1.GetHashCode() : 0);

        // int properties
        hashCode = (hashCode * 397) ^ intProperty;
        return hashCode;
    }
}

397 ist eine Primzahl von ausreichender Größe der Ergebnisvariable zu veranlassen, die Bits des Hash etwas überzulaufen und mischen, um eine bessere Verteilung des Hash-Codes bereitstellt. Ansonsten gibt es nichts besonderes in 397, die es von anderen Primzahlen in der gleichen Größenordnung unterscheidet.

Ach ja, wie Gary Shutler wies darauf hin:

return str1.GetHashCode() + str2.GetHashCode();

Kann überlaufen. Sie könnten Gießen zu lange versuchen, als Artem vorgeschlagen, oder man könnte die Aussage in dem ungeprüften Schlüsselwort umgeben:

return unchecked(str1.GetHashCode() + str2.GetHashCode());

Versuchen Sie diese:

(((long)str1.GetHashCode()) + ((long)str2.GetHashCode())).GetHashCode()

Viele Möglichkeiten. Z. B.

return str1.GetHashCode() ^ str1.GetHashCode()

Vielleicht so etwas wie str1.GetHashCode () + str2.GetHashCode ()? oder (str1.GetHashCode () + str2.GetHashCode ()) / 2? Auf diese Weise wäre es das gleiche sein, unabhängig davon, ob str1 und str2 vertauscht ....

sortieren, dann verketten sie:

return ((str1.CompareTo(str2) < 1) ? str1 + str2 : str2 + str1)
    .GetHashCode();

GetHashCode das Ergebnis sein soll:

  1. So schnell wie möglich.
  2. So einzigartig wie möglich.

Lager diejenigen im Auge, würde ich mit etwas so aus:

if (str1 == null)
    if (str2 == null)
        return 0;
    else
       return str2.GetHashCode();
else
    if (str2 == null)
        return str1.GetHashCode();
    else
       return ((ulong)str1.GetHashCode() | ((ulong)str2.GetHashCode() << 32)).GetHashCode();

Edit: vergessen, die NULL-Werte. Code festgelegt.

Zu kompliziert, und vergisst nulls usw. Dies ist für Dinge wie Bucketing verwendet wird, so dass Sie sich mit so etwas wie

bekommen
if (null != str1) {
    return str1.GetHashCode();
}
if (null != str2) {
    return str2.GetHashCode();
}
//Not sure what you would put here, some constant value will do
return 0;

Dies ist vorgespannt, unter der Annahme, dass str1 nicht wahrscheinlich ist, in einem ungewöhnlich hohen Anteil an Fällen üblich sein.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top