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 Typ hat, besteht in der einfachen Rückgabe Int32 dieses Werts. 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 in der Kombination dieser Felder mithilfe eines XOR (eXclusive OR) Vorgangs, 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 Kollisionen generiert werden, als wünschenswert sind. Eine Reihe von Lösungen ist verfügbar, sodass Hashcodes in diesen Fällen nicht identisch sind. Eine besteht in der Rückgabe des Hashcodes eines Tuple -Objekts, der die Reihenfolge der einzelnen Felde widerspiegelt. Das folgende Beispiel zeigt eine mögliche Implementierung, die die -Klasse Tuple<T1,T2> verwendet. Beachten Sie jedoch, dass sich der Leistungsaufwand beim Instanziieren eines Objekts erheblich auf die Gesamtleistung einer Anwendung auswirken kann, die eine große Anzahl von Objekten Tuple 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 aufeinander folgender Felder um zwei oder mehr Bits nach links verschiebt. Optimalerweise sollten Bits, die über Bit 31 verschoben wurden, umschließen und nicht verworfen werden. Da Bits sowohl in C# als auch in Visual Basic von den Linksverschiebungsoperatoren verworfen werden, muss eine 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 -Struktur zu berechnen, die Point in den vorherigen Beispielen verwendet wurde.

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 zum Einfügen und Identifizieren eines Objekts in einer hashbasierten Auflistung wie der -Klasse, der -Klasse oder einem von der -Klasse abgeleiteten Dictionary<TKey,TValue> Hashtable Typ verwendet DictionaryBase wird. Die GetHashCode -Methode bietet diesen Hashcode für Algorithmen, 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 Eintrag Hashfunktion auf Wikipedia.

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

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

  • Sie sollten keinen Hashcode außerhalb der Anwendungsdomäne beibehalten oder verwenden, in der er erstellt wurde, da dasselbe Objekt anwendungsdomänen-, prozess- und plattformübergreifend hashen kann.

Warnung

Ein Hashcode ist für die 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üsselierten Auflistung abzurufen.
  • Senden Sie keine Hashcodes über Anwendungsdomänen oder Prozesse hinweg. In einigen Fällen können Hashcodes pro Prozess oder 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 Klasse, die von der -Klasse oder der System.Security.Cryptography.HashAlgorithm -Klasse System.Security.Cryptography.KeyedHashAlgorithm abgeleitet ist.
  • Testen Sie nicht auf Gleichheit von Hashcodes, um zu bestimmen, ob zwei Objekte gleich sind. (Ungleiche Objekte können identische Hashcodes haben.) Um die Gleichheit zu testen, rufen Sie die ReferenceEquals - oder Equals -Methode auf.

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

Warnung

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

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

Hinweise zur Windows Runtime

Wenn Sie die -Methode für eine Klasse in der Windows Runtime aufrufen, wird das Standardverhalten für Klassen bereitgestellt, die GetHashCode nicht GetHashCode überschreiben. Dies ist Teil der Unterstützung, die .NET Framework für die Windows Runtime bietet (siehe .NET Framework-Unterstützung für Windows Store-Appsund Windows Runtime). Klassen in der Windows Runtime erben nicht und Object implementieren derzeit keine GetHashCode . Sie scheinen jedoch über die Methoden , und zu verfügen, wenn Sie sie ToString Equals(Object) in Ihrem C#- oder Visual Basic-Code verwenden, und die .NET Framework stellt das Standardverhalten für diese GetHashCode Methoden zur Anwendung.

Hinweis

Windows Laufzeitklassen, die in C# oder Visual Basic, können die -Methode GetHashCode ü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 spezifisch für jeden Typ und müssen zur Eindeutigkeit mindestens eines der Instanzfelder als Eingabe verwenden. Hashcodes sollten nicht mithilfe der Werte statischer Felder berechnet werden.

Für klassen, die von abgeleitet werden, kann die -Methode nur dann an die Basisklassenimplementierung delegieren, wenn die abgeleitete Klasse Gleichheit als Object GetHashCode GetHashCode() Verweisgleichheit definiert. Die Standardimplementierung von für Verweistypen gibt einen Hashcode zurück, der dem von GetHashCode() der -Methode zurückgegebenen GetHashCode(Object) Code entspricht. Sie können GetHashCode() für unveränderliche Verweistypen überschreiben. Im Allgemeinen sollten Sie für veränderliche 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.

– Für eine optimale Leistung 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, auch 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 ebenfalls 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