Object.GetHashCode Methode

Definition

Fungiert als Standardhashfunktion.

public:
 virtual int GetHashCode();
public virtual int GetHashCode ();
abstract member GetHashCode : unit -> int
override this.GetHashCode : unit -> int
Public Overridable Function GetHashCode () As Integer

Gibt zurück

Int32

Ein Hashcode für das aktuelle Objekt.

Beispiele

Eine der einfachsten Möglichkeiten, einen Hashcode für einen numerischen Wert zu berechnen, der denselben oder einen kleineren Bereich als der Int32 Typ hat, besteht darin, einfach diesen Wert zurückzugeben. Das folgende Beispiel zeigt eine solche Implementierung für eine Number -Struktur.

using System;

public struct Number
{
   private int n;

   public Number(int value)
   {
      n = value;
   }

   public int Value
   {
      get { return n; }
   }

   public override bool Equals(Object obj)
   {
      if (obj == null || ! (obj is Number))
         return false;
      else
         return n == ((Number) obj).n;
   }

   public override int GetHashCode()
   {
      return n;
   }

   public override string ToString()
   {
      return n.ToString();
   }
}

public class Example
{
   public static void Main()
   {
      Random rnd = new Random();
      for (int ctr = 0; ctr <= 9; ctr++) {
         int randomN = rnd.Next(Int32.MinValue, Int32.MaxValue);
         Number n = new Number(randomN);
         Console.WriteLine("n = {0,12}, hash code = {1,12}", n, n.GetHashCode());
      }
   }
}
// The example displays output like the following:
//       n =   -634398368, hash code =   -634398368
//       n =   2136747730, hash code =   2136747730
//       n =  -1973417279, hash code =  -1973417279
//       n =   1101478715, hash code =   1101478715
//       n =   2078057429, hash code =   2078057429
//       n =   -334489950, hash code =   -334489950
//       n =    -68958230, hash code =    -68958230
//       n =   -379951485, hash code =   -379951485
//       n =    -31553685, hash code =    -31553685
//       n =   2105429592, hash code =   2105429592
Public Structure Number
   Private n As Integer

   Public Sub New(value As Integer)
      n = value
   End Sub

   Public ReadOnly Property Value As Integer
      Get
         Return n
      End Get
   End Property
   
   Public Overrides Function Equals(obj As Object) As Boolean
      If obj Is Nothing OrElse Not TypeOf obj Is Number Then
         Return False
      Else
         Return n = CType(obj, Number).n
      End If
   End Function      
   
   Public Overrides Function GetHashCode() As Integer
      Return n
   End Function
   
   Public Overrides Function ToString() As String
      Return n.ToString()
   End Function
End Structure

Module Example
   Public Sub Main()
      Dim rnd As New Random()
      For ctr As Integer = 0 To 9
         Dim randomN As Integer = rnd.Next(Int32.MinValue, Int32.MaxValue)
         Dim n As New Number(randomN)
         Console.WriteLine("n = {0,12}, hash code = {1,12}", n, n.GetHashCode())
      Next
   End Sub
End Module
' The example displays output like the following:
'       n =   -634398368, hash code =   -634398368
'       n =   2136747730, hash code =   2136747730
'       n =  -1973417279, hash code =  -1973417279
'       n =   1101478715, hash code =   1101478715
'       n =   2078057429, hash code =   2078057429
'       n =   -334489950, hash code =   -334489950
'       n =    -68958230, hash code =    -68958230
'       n =   -379951485, hash code =   -379951485
'       n =    -31553685, hash code =    -31553685
'       n =   2105429592, hash code =   2105429592

Häufig verfügt ein Typ über mehrere Datenfelder, die an der Generierung des Hashcodes beteiligt sein können. Eine Möglichkeit zum Generieren eines Hashcodes besteht darin, diese Felder mithilfe eines Vorgangs zu XOR (eXclusive OR) kombinieren, wie im folgenden Beispiel gezeigt.

using System;

// A type that represents a 2-D point.
public struct Point
{
    private int x;
    private int y;

    public Point(int x, int y)
    {
       this.x = x;
       this.y = y;
    }

    public override bool Equals(Object obj)
    {
       if (! (obj is Point)) return false;

       Point p = (Point) obj;
       return x == p.x & y == p.y;
    }

    public override int GetHashCode()
    {
        return x ^ y;
    }
}

public class Example
{
   public static void Main()
   {
      Point pt = new Point(5, 8);
      Console.WriteLine(pt.GetHashCode());

      pt = new Point(8, 5);
      Console.WriteLine(pt.GetHashCode());
   }
}
// The example displays the following output:
//       13
//       13
' A type that represents a 2-D point.
Public Structure Point
    Private x As Integer
    Private y As Integer

    Public Sub New(x As Integer, y As Integer)
       Me.x = x
       Me.y = y
    End Sub
    
    Public Overrides Function Equals(obj As Object) As Boolean
       If Not TypeOf obj Is Point Then Return False
       
       Dim p As Point = CType(obj, Point)
       Return x = p.x And y = p.y
    End Function
    
    Public Overrides Function GetHashCode() As Integer 
        Return x Xor y
    End Function 
End Structure 

Public Module Example
   Public Sub Main() 
      Dim pt As New Point(5, 8)
      Console.WriteLine(pt.GetHashCode())
        
      pt = New Point(8, 5)
      Console.WriteLine(pt.GetHashCode())
   End Sub 
End Module

Im vorherigen Beispiel wird der gleiche Hashcode für (n1, n2) und (n2, n1) zurückgegeben, sodass möglicherweise mehr Konflikte generiert werden, als wünschenswert sind. Es sind eine Reihe von Lösungen verfügbar, sodass Hashcodes in diesen Fällen nicht identisch sind. Eine besteht darin, den Hashcode eines Tuple -Objekts zurückzugeben, das die Reihenfolge der einzelnen Felder widerspiegelt. Das folgende Beispiel zeigt eine mögliche Implementierung, die die Tuple<T1,T2> -Klasse verwendet. Beachten Sie jedoch, dass sich der Leistungsaufwand für die Instanziierung eines Tuple -Objekts erheblich auf die Gesamtleistung einer Anwendung auswirken kann, die eine große Anzahl von Objekten in Hashtabellen speichert.

using System;

public struct Point
{
    private int x;
    private int y;

    public Point(int x, int y)
    {
       this.x = x;
       this.y = y;
    }

    public override bool Equals(Object obj)
    {
        if (obj is Point)
        {
            Point p = (Point) obj;
            return x == p.x & y == p.y;
        }
        else
        {
            return false;
        }      
    }

    public override int GetHashCode()
    {
        return Tuple.Create(x, y).GetHashCode();
    }
}

public class Example
{
   public static void Main()
   {
        Point pt = new Point(5, 8);
        Console.WriteLine(pt.GetHashCode());

        pt = new Point(8, 5);
        Console.WriteLine(pt.GetHashCode());
   }
}
// The example displays the following output:
//       173
//       269
Public Structure Point
    Private x As Integer
    Private y As Integer

    Public Sub New(x As Integer, y As Integer)
       Me.x = x
       Me.y = y
    End Sub
    
    Public Overrides Function Equals(obj As Object) As Boolean
       If Not TypeOf obj Is Point Then Return False
       
       Dim p As Point = CType(obj, Point)
       Return x = p.x And y = p.y
    End Function
    
    Public Overrides Function GetHashCode() As Integer 
        Return Tuple.Create(x, y).GetHashCode()
    End Function 
End Structure 

Public Module Example
    Public Sub Main() 
        Dim pt As New Point(5, 8)
        Console.WriteLine(pt.GetHashCode())
        
        pt = New Point(8, 5)
        Console.WriteLine(pt.GetHashCode())
    End Sub 
End Module         
' The example displays the following output:
'       173
'       269

Eine zweite alternative Lösung besteht darin, die einzelnen Hashcodes zu gewichten, indem die Hashcodes aufeinanderfolgender Felder um zwei oder mehr Bits nach links verschoben werden. Optimalerweise sollten Bits, die über Bit 31 hinaus verschoben wurden, umschließen, anstatt verworfen zu werden. Da Bits von den Left-Shift-Operatoren sowohl in C# als auch in Visual Basic verworfen werden, muss hierfür eine Left Shift-and-Wrap-Methode wie die folgende erstellt werden:

public int ShiftAndWrap(int value, int positions)
{
    positions = positions & 0x1F;

    // Save the existing bit pattern, but interpret it as an unsigned integer.
    uint number = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
    // Preserve the bits to be discarded.
    uint wrapped = number >> (32 - positions);
    // Shift and wrap the discarded bits.
    return BitConverter.ToInt32(BitConverter.GetBytes((number << positions) | wrapped), 0);
}
Public Function ShiftAndWrap(value As Integer, positions As Integer) As Integer
   positions = positions And &h1F
   
   ' Save the existing bit pattern, but interpret it as an unsigned integer.
   Dim number As UInteger = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0)
   ' Preserve the bits to be discarded.
   Dim wrapped AS UInteger = number >> (32 - positions)
   ' Shift and wrap the discarded bits.
   Return BitConverter.ToInt32(BitConverter.GetBytes((number << positions) Or wrapped), 0)
End Function

Im folgenden Beispiel wird dann diese Shift-and-Wrap-Methode verwendet, um den Hashcode der Point in den vorherigen Beispielen verwendeten Struktur zu berechnen.

using System;

public struct Point
{
    private int x;
    private int y;

    public Point(int x, int y)
    {
       this.x = x;
       this.y = y;
    }

    public override bool Equals(Object obj)
    {
       if (!(obj is Point)) return false;

       Point p = (Point) obj;
       return x == p.x & y == p.y;
    }

    public override int GetHashCode()
    {
        return ShiftAndWrap(x.GetHashCode(), 2) ^ y.GetHashCode();
    }

    private int ShiftAndWrap(int value, int positions)
    {
        positions = positions & 0x1F;

        // Save the existing bit pattern, but interpret it as an unsigned integer.
        uint number = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
        // Preserve the bits to be discarded.
        uint wrapped = number >> (32 - positions);
        // Shift and wrap the discarded bits.
        return BitConverter.ToInt32(BitConverter.GetBytes((number << positions) | wrapped), 0);
    }
}

public class Example
{
   public static void Main()
   {
        Point pt = new Point(5, 8);
        Console.WriteLine(pt.GetHashCode());

        pt = new Point(8, 5);
        Console.WriteLine(pt.GetHashCode());
   }
}
// The example displays the following output:
//       28
//       37
Public Structure Point
    Private x As Integer
    Private y As Integer

    Public Sub New(x As Integer, y As Integer)
       Me.x = x
       Me.y = y
    End Sub
    
    Public Overrides Function Equals(obj As Object) As Boolean
       If Not TypeOf obj Is Point Then Return False
       
       Dim p As Point = CType(obj, Point)
       Return x = p.x And y = p.y
    End Function
    
    Public Overrides Function GetHashCode() As Integer 
        Return ShiftAndWrap(x.GetHashCode(), 2) XOr y.GetHashCode()
    End Function 
    
    Private Function ShiftAndWrap(value As Integer, positions As Integer) As Integer
        positions = positions And &h1F
      
        ' Save the existing bit pattern, but interpret it as an unsigned integer.
        Dim number As UInteger = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0)
        ' Preserve the bits to be discarded.
        Dim wrapped AS UInteger = number >> (32 - positions)
        ' Shift and wrap the discarded bits.
        Return BitConverter.ToInt32(BitConverter.GetBytes((number << positions) Or wrapped), 0)
    End Function
End Structure 

Module Example
   Public Sub Main()
        Dim pt As New Point(5, 8)
        Console.WriteLine(pt.GetHashCode())
        
        pt = New Point(8, 5)
        Console.WriteLine(pt.GetHashCode())
   End Sub
End Module
' The example displays the following output:
'       28
'       37

Hinweise

Ein Hashcode ist ein numerischer Wert, der verwendet wird, um ein Objekt in einer hashbasierten Auflistung einzufügen und zu identifizieren, z. B. die Dictionary<TKey,TValue> -Klasse, die Hashtable -Klasse oder einen von der -Klasse abgeleiteten DictionaryBase Typ. Die GetHashCode -Methode stellt diesen Hashcode für Algorithmen bereit, die schnelle Überprüfungen der Objektgleichheit benötigen.

Hinweis

Informationen zur Verwendung von Hashcodes in Hashtabellen und zu einigen zusätzlichen Hashcodealgorithmen finden Sie im Hashfunktionseintrag auf Wikipedia.

Zwei Objekte, die gleich sind, geben hashcodes zurück, die gleich sind. Das Gegenteil ist jedoch nicht wahr: Gleiche Hashcodes implizieren keine Objektgleichheit, da unterschiedliche (ungleiche) Objekte identische Hashcodes aufweisen können. Darüber hinaus garantiert .NET nicht die Standardimplementierungen der GetHashCode -Methode, und der wert, den diese Methode zurückgibt, kann zwischen .NET-Implementierungen, z. B. verschiedenen Versionen von .NET Framework und .NET Core, und Plattformen wie 32-Bit- und 64-Bit-Plattformen abweichen. Verwenden Sie aus diesen Gründen nicht die Standardimplementierung dieser Methode als eindeutigen Objektbezeichner für Hashingzwecke. Daraus ergeben sich zwei Konsequenzen:

  • Sie sollten nicht davon ausgehen, dass gleiche Hashcodes Objektgleichheit implizieren.

  • Sie sollten einen Hashcode niemals außerhalb der Anwendungsdomäne beibehalten oder verwenden, in der er erstellt wurde, da das gleiche Objekt einen Hash über Anwendungsdomänen, Prozesse und Plattformen hinweg erstellen kann.

Warnung

Ein Hashcode ist für eine effiziente Einfügung und Suche in Auflistungen vorgesehen, die auf einer Hashtabelle basieren. Ein Hashcode ist kein dauerhafter Wert. Aus diesem Grund:

  • Serialisieren Sie keine Hashcodewerte, und speichern Sie sie nicht in Datenbanken.
  • Verwenden Sie den Hashcode nicht als Schlüssel, um ein Objekt aus einer Schlüsselauflistung abzurufen.
  • Senden Sie keine Hashcodes über Anwendungsdomänen oder Prozesse hinweg. In einigen Fällen können Hashcodes pro Prozess oder pro Anwendungsdomäne berechnet werden.
  • Verwenden Sie nicht den Hashcode anstelle eines Werts, der von einer kryptografischen Hashfunktion zurückgegeben wird, wenn Sie einen kryptografisch starken Hash benötigen. Verwenden Sie für kryptografische Hashes eine von der - oder -Klasse abgeleitete System.Security.Cryptography.HashAlgorithm System.Security.Cryptography.KeyedHashAlgorithm Klasse.
  • Testen Sie nicht auf Gleichheit von Hashcodes, um zu bestimmen, ob zwei Objekte gleich sind. (Ungleiche Objekte können identische Hashcodes aufweisen.) Um die Gleichheit zu testen, rufen Sie die ReferenceEquals - oder Equals -Methode auf.

Die GetHashCode Methode kann von einem abgeleiteten Typ überschrieben werden. Wenn GetHashCode nicht überschrieben wird, werden Hashcodes für Verweistypen berechnet, indem die Object.GetHashCode -Methode der Basisklasse aufgerufen wird, die einen Hashcode basierend auf dem Verweis eines Objekts berechnet. Weitere Informationen finden Sie unter RuntimeHelpers.GetHashCode . Anders ausgedrückt: Zwei Objekte, für die die ReferenceEquals Methode zurückgibt, true verfügen über identische Hashcodes. Wenn Werttypen nicht GetHashCode überschreiben, verwendet die ValueType.GetHashCode -Methode der Basisklasse Reflektion, um den Hashcode basierend auf den Werten der Felder des Typs zu berechnen. Anders ausgedrückt: Werttypen, deren Felder über gleiche Werte verfügen, weisen gleiche Hashcodes auf. Weitere Informationen zum Überschreiben von GetHashCode finden Sie im Abschnitt "Hinweise zu Erben".

Warnung

Wenn Sie die GetHashCode -Methode überschreiben, sollten Sie auch überschreiben Equals und umgekehrt. Wenn die überschriebene Equals Methode true zurückgibt, wenn zwei Objekte auf Gleichheit getestet werden, muss die überschriebene GetHashCode Methode den gleichen Wert für die beiden Objekte zurückgeben.

Wenn ein Objekt, das als Schlüssel in einer Hashtabelle verwendet wird, keine nützliche Implementierung von GetHashCode bereitstellt, können Sie einen Hashcodeanbieter angeben, indem Sie eine IEqualityComparer Implementierung für eine der Überladungen des Hashtable Klassenkonstruktors bereitstellen.

Hinweise zur Windows Runtime

Wenn Sie die GetHashCode -Methode für eine Klasse in der Windows Runtime aufrufen, stellt sie das Standardverhalten für Klassen bereit, die nicht überschreiben. GetHashCode Dies ist Teil der Unterstützung, die der .NET Framework für die Windows Runtime bereitstellt (siehe .NET Framework Support for Windows Store Apps and Windows Runtime). Klassen in der Windows Runtime erben nicht Object und implementieren derzeit keine GetHashCode . Sie verfügen jedoch scheinbar über die Methoden , und , ToString wenn Sie sie in Ihrem Equals(Object) GetHashCode C#- oder Visual Basic-Code verwenden, und die .NET Framework stellt das Standardverhalten für diese Methoden bereit.

Hinweis

Windows Laufzeitklassen, die in C# oder Visual Basic geschrieben sind, können die GetHashCode -Methode überschreiben.

Hinweise für Vererber

Eine Hashfunktion wird verwendet, um schnell eine Zahl (Hashcode) zu generieren, die dem Wert eines Objekts entspricht. Hashfunktionen sind in der Regel für jeden Typ spezifisch und müssen aus Gründen der Eindeutigkeit mindestens eines der Instanzfelder als Eingabe verwenden. Hashcodes sollten nicht mithilfe der Werte statischer Felder berechnet werden.

Bei klassen, die von abgeleitet Object werden, kann die GetHashCode Methode nur dann an die Basisklassenimplementierungen delegieren, wenn die GetHashCode() abgeleitete Klasse Gleichheit als Verweisgleichheit definiert. Die Standardimplementierungen von GetHashCode() für Verweistypen gibt einen Hashcode zurück, der dem von der -Methode zurückgegebenen Hashcode GetHashCode(Object) entspricht. Sie können GetHashCode() für unveränderliche Verweistypen überschreiben. Im Allgemeinen sollten Sie für änderbare Verweistypen nur dann außer Kraft setzen, GetHashCode() wenn: – Sie können den Hashcode aus Feldern berechnen, die nicht veränderlich sind. Oder – Sie können sicherstellen, dass sich der Hashcode eines änderbaren Objekts nicht ändert, während das Objekt in einer Auflistung enthalten ist, die auf seinem Hashcode basiert.

Andernfalls könnten Sie denken, dass das änderbare Objekt in der Hashtabelle verloren geht. Wenn Sie sich GetHashCode() für einen änderbaren Verweistyp entscheiden, sollten Sie in der Dokumentation deutlich machen, dass Benutzer Ihres Typs objektwerte nicht ändern sollten, während das Objekt in einer Hashtabelle gespeichert wird.

Stellt für Werttypen GetHashCode() eine Standardmäßige Hashcodeimplementierungen bereit, die Reflektion verwenden. Sie sollten erwägen, sie zu überschreiben, um eine bessere Leistung zu erzielen.

Weitere Informationen und Beispiele, die Hashcodes auf unterschiedliche Weise berechnen, finden Sie im Abschnitt Beispiele.

Eine Hashfunktion muss die folgenden Eigenschaften aufweisen: – Wenn zwei Objekte als gleich verglichen werden, muss die GetHashCode() Methode für jedes Objekt den gleichen Wert zurückgeben. Wenn zwei Objekte jedoch nicht als gleich verglichen werden, müssen die GetHashCode() Methoden für die beiden Objekte keine unterschiedlichen Werte zurückgeben.

– Die GetHashCode() -Methode für ein Objekt muss konsistent den gleichen Hashcode zurückgeben, solange keine Änderung am Objektzustand vorgenommen wird, der den Rückgabewert der System.Object.Equals-Methode des Objekts bestimmt. Beachten Sie, dass dies nur für die aktuelle Ausführung einer Anwendung gilt und dass ein anderer Hashcode zurückgegeben werden kann, wenn die Anwendung erneut ausgeführt wird.

– Um eine optimale Leistung zu erzielen, sollte eine Hashfunktion eine gleichmäßige Verteilung für alle Eingaben generieren, einschließlich stark gruppierter Eingaben. Dies hat zur Folge, dass kleine Änderungen am Objektzustand zu umfangreichen Änderungen am resultierenden Hashcode führen sollten, um eine optimale Leistung der Hashtabelle zu erzielen.

– Hashfunktionen sollten kostengünstig zu berechnen sein.

– Die GetHashCode() -Methode sollte keine Ausnahmen auslösen.

Beispielsweise gibt die Implementierung der GetHashCode() -Methode, die von der -Klasse bereitgestellt wird, String identische Hashcodes für identische Zeichenfolgenwerte zurück. Daher geben zwei String Objekte den gleichen Hashcode zurück, wenn sie denselben Zeichenfolgenwert darstellen. Außerdem verwendet die -Methode alle Zeichen in der Zeichenfolge, um eine halbwegs zufällig verteilte Ausgabe zu generieren, selbst wenn die Eingabe in bestimmten Bereichen gruppiert ist (z. B. verfügen viele Benutzer möglicherweise über Zeichenfolgen, die nur die unteren 128 ASCII-Zeichen enthalten, obwohl eine Zeichenfolge eines der 65.535 Unicode-Zeichen enthalten kann).

Die Bereitstellung einer guten Hashfunktion für eine Klasse kann sich erheblich auf die Leistung beim Hinzufügen dieser Objekte zu einer Hashtabelle auswirken. In einer Hashtabelle mit Schlüsseln, die eine gute Implementierung einer Hashfunktion bereitstellen, dauert die Suche nach einem Element konstant lange (z. B. ein O(1)-Vorgang). In einer Hashtabelle mit einer schlechten Implementierung einer Hashfunktion hängt die Leistung einer Suche von der Anzahl der Elemente in der Hashtabelle ab (z. B. einem O( n ) -Vorgang, wobei n die Anzahl der Elemente in der Hashtabelle ist. Ein böswilliger Benutzer kann Unter den folgenden Bedingungen Daten eingeben, die die Anzahl von Kollisionen erhöhen, wodurch die Leistung von Anwendungen, die von Hashtabellen abhängig sind, erheblich beeinträchtigt werden kann: – Wenn Hashfunktionen häufige Konflikte erzeugen.

– Wenn ein großer Teil der Objekte in einer Hashtabelle Hashcodes erzeugt, die gleich oder ungefähr gleich sind.

– Wenn Benutzer die Daten eingeben, aus denen der Hashcode berechnet wird.

Abgeleitete Klassen, die GetHashCode() überschreiben, müssen auch Equals(Object) überschreiben, um sicherzustellen, dass zwei als gleich betrachtete Objekte denselben Hashcode aufweisen. Andernfalls funktioniert der Hashtable Typ möglicherweise nicht ordnungsgemäß.

Gilt für

Siehe auch