Object.GetHashCode Metod

Definition

Fungerar som standard-hash-funktion.

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

Returer

En hash-kod för det aktuella objektet.

Exempel

Ett av de enklaste sätten Int32 att beräkna en hashkod för ett numeriskt värde som har samma eller ett mindre intervall än typen är att helt enkelt returnera det värdet. I följande exempel visas en sådan implementering för en 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 Example1
{
   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
open System

[<Struct; CustomEquality; NoComparison>]
type Number(value: int) =
    member _.Value = value

    override _.Equals(obj) =
        match obj with
        | :? Number as n ->
            n.Value = value
        | _ -> false

    override _.GetHashCode() =
        value

    override _.ToString() =
        string value

let rnd = Random()
for _ = 0 to 9 do
    let randomN = rnd.Next(Int32.MinValue, Int32.MaxValue)
    let n = Number randomN
    printfn $"n = {n,12}, hash code = {n.GetHashCode(),12}"
// 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 Example1
    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

Ofta har en typ flera datafält som kan delta i genereringen av hash-koden. Ett sätt att generera en hashkod är att kombinera dessa fält med hjälp av en XOR (eXclusive OR) åtgärd, som du ser i följande exempel.

using System;

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

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

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

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

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

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

      pt = new Point2(8, 5);
      Console.WriteLine(pt.GetHashCode());
   }
}
// The example displays the following output:
//       13
//       13
// A type that represents a 2-D point.
[<Struct; CustomEquality; NoComparison>]
type Point(x: int, y: int) =
    member _.X = x
    member _.Y = y

    override _.Equals(obj) =
        match obj with
        | :? Point as p ->
            x = p.X && y = p.Y
        | _ ->
            false

    override _.GetHashCode() =
        x ^^^ y

let pt = Point(5, 8)
printfn $"{pt.GetHashCode()}"

let pt2 = Point(8, 5)
printfn $"{pt2.GetHashCode()}"

// The example displays the following output:
//       13
//       13
' A type that represents a 2-D point.
Public Structure Point3
    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 Point3 Then Return False

        Dim p As Point3 = CType(obj, Point3)
        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 Example3
    Public Sub Main()
        Dim pt As New Point3(5, 8)
        Console.WriteLine(pt.GetHashCode())

        pt = New Point3(8, 5)
        Console.WriteLine(pt.GetHashCode())
    End Sub
End Module

I föregående exempel returneras samma hashkod för (n1, n2) och (n2, n1), så det kan generera fler kollisioner än vad som är önskvärt. På .NET 5+ är den rekommenderade lösningen att använda HashCode.Combine. Det undviker symmetriproblemet och skapar en välfördelad hashkod utan att behöva skapa objektet Tuple.

using System;

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

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

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

    public override int GetHashCode()
    {
        return HashCode.Combine(x, y);
    }
}

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

        pt = new Point3(8, 5);
        Console.WriteLine(pt.GetHashCode());
   }
}
// The example displays output similar to the following.
// Note: HashCode.Combine results are not stable across .NET versions.
//       185727722
//       -363254492
[<Struct; CustomEquality; NoComparison>]
type Point(x: int, y: int) =
    member _.X = x
    member _.Y = y

    override _.Equals(obj) =
        match obj with
        | :? Point as p ->
            x = p.X && y = p.Y
        | _ -> 
            false

    override _.GetHashCode() =
        System.HashCode.Combine(x, y)

let pt = Point(5, 8)
printfn $"{pt.GetHashCode()}"

let pt2 = Point(8, 5)
printfn $"{pt2.GetHashCode()}"
// The example displays output similar to the following.
// Note: HashCode.Combine results are not stable across .NET versions.
//       185727722
//       -363254492
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 HashCode.Combine(x, 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         
' The example displays output similar to the following.
' Note: HashCode.Combine results are not stable across .NET versions.
'       185727722
'       -363254492

Kommentarer

Metoden GetHashCode tillhandahåller en hash-kod för algoritmer som behöver snabbkontroller av objektjämlikhet. En hash-kod är ett numeriskt värde som används för att infoga och identifiera ett objekt i en hash-baserad samling, till exempel Dictionary<TKey,TValue> klassen, Hashtable klassen eller en typ som härletts från DictionaryBase klassen.

Note

Information om hur hashkoder används i hash-tabeller och för ytterligare hashkodalgoritmer finns i hash-funktionsposten i Wikipedia.

Två objekt som är lika returnerar hashkoder som också är lika. Det omvända är dock inte sant: lika hashkoder innebär inte objektjämlikhet, eftersom olika (ojämlika) objekt kan ha identiska hash-koder. Dessutom garanterar .NET inte standardimplementeringen av metoden GetHashCode, och värdet som den här metoden returnerar kan skilja sig mellan .NET implementeringar och plattformar, till exempel mellan 32-bitars- och 64-bitarsplattformar. Av dessa skäl ska du inte använda standardimplementeringen av den här metoden som ett unikt objektidentifierare för hashningsändamål. Två konsekvenser följer av detta:

  • Du bör inte anta att lika hashkoder innebär objektjämlikhet.
  • Du bör aldrig bevara eller använda en hash-kod utanför programdomänen där den skapades, eftersom samma objekt kan hasha mellan programdomäner, processer och plattformar.

Varning

En hash-kod är avsedd för effektiv infogning och sökning i samlingar som baseras på en hash-tabell. En hash-kod är inte ett permanent värde. Av den anledningen:

  • Serialisera inte hash-kodvärden eller lagra dem i databaser.
  • Använd inte hash-koden som nyckel för att hämta ett objekt från en nyckelsamling.
  • Skicka inte hash-koder mellan programdomäner eller processer. I vissa fall kan hashkoder beräknas per process eller per programdomän.
  • Använd inte hashkoden i stället för ett värde som returneras av en kryptografisk hashfunktion om du behöver en kryptografiskt stark hash. För kryptografiska hashar använder du en klass som härletts från System.Security.Cryptography.HashAlgorithm klassen eller System.Security.Cryptography.KeyedHashAlgorithm .
  • Testa inte för likhet mellan hashkoder för att avgöra om två objekt är lika. (Ojämlika objekt kan ha identiska hash-koder.) För att testa jämlikhet, anropa metoden ReferenceEquals eller Equals.

Metoden GetHashCode kan åsidosättas av en härledd typ. Om GetHashCode inte åsidosätts beräknas hash-koder för referenstyper genom att anropa Object.GetHashCode metoden för basklassen, som beräknar en hash-kod baserat på ett objekts referens. Mer information RuntimeHelpers.GetHashCodefinns i . Med andra ord har två objekt som ReferenceEquals metoden returnerar true identiska hash-koder för. Om värdetyper inte åsidosätter GetHashCodeValueType.GetHashCode använder metoden för basklassen reflektion för att beräkna hash-koden baserat på värdena för typens fält. Med andra ord har värdetyper vars fält har lika värden lika med hashkoder. Mer information om åsidosättande av GetHashCode finns i avsnittet "Anteckningar till arvtagare".

Varning

Om du åsidosätter GetHashCode metoden bör du också åsidosätta Equals, och vice versa. Om den åsidosatta Equals metoden returnerar true när två objekt testas för likhet måste den åsidosatta GetHashCode metoden returnera samma värde för de två objekten.

Om ett objekt som används som en nyckel i en hash-tabell inte ger någon användbar implementering av GetHashCodekan du ange en hash-kodprovider genom att ange en IEqualityComparer implementering till en av överlagringarna av Hashtable klasskonstruktorn.

Anteckningar till arvingar

En hash-funktion används för att snabbt generera ett tal (hashkod) som motsvarar värdet för ett objekt. Hash-funktioner är vanligtvis specifika för varje typ och måste för unikhet använda minst ett av instansfälten som indata. Hash-koder bör inte beräknas med hjälp av värdena för statiska fält.

För klasser som härleds från ObjectGetHashCode kan metoden endast delegera till basklassimplementeringen GetHashCode() om den härledda klassen definierar likhet som referensjämlikhet. Standardimplementeringen av GetHashCode() för referenstyper returnerar en hash-kod som motsvarar den som returneras av GetHashCode(Object) metoden. Du kan åsidosätta GetHashCode() för oföränderliga referenstyper. För föränderliga referenstyper bör du i allmänhet endast åsidosätta GetHashCode() om:

  • Du kan beräkna hash-koden från fält som inte kan ändras. Eller

  • Du kan se till att hash-koden för ett föränderligt objekt inte ändras medan objektet finns i en samling som förlitar sig på dess hash-kod.

Annars kanske du tror att det föränderliga objektet går förlorat i hash-tabellen. Om du väljer att åsidosätta GetHashCode() för en föränderlig referenstyp bör dokumentationen klargöra att användare av din typ inte ska ändra objektvärden medan objektet lagras i en hash-tabell.

För värdetyper GetHashCode() tillhandahåller en standardimplementering av hash-kod som använder reflektion. Du bör överväga att åsidosätta det för bättre prestanda.

Mer information och exempel som beräknar hashkoder på flera olika sätt finns i avsnittet Exempel.

En hash-funktion måste ha följande egenskaper:

  • Om två objekt jämförs som lika GetHashCode() måste metoden för varje objekt returnera samma värde. Men om två objekt inte jämförs som lika behöver metoderna för de två objekten GetHashCode() inte returnera olika värden.

  • Metoden GetHashCode() för ett objekt måste konsekvent returnera samma hash-kod så länge det inte finns någon ändring i objekttillståndet som avgör returvärdet för objektets System.Object.Equals-metod . Observera att detta endast gäller för den aktuella körningen av ett program och att en annan hashkod kan returneras om programmet körs igen.

  • För bästa prestanda bör en hash-funktion generera en jämn distribution för alla indata, inklusive indata som är kraftigt klustrade. En implikation är att små ändringar i objekttillståndet bör resultera i stora ändringar i den resulterande hashkoden för bästa hash-tabellprestanda.

  • Hash-funktioner bör vara billiga att beräkna.

  • Metoden GetHashCode() bör inte utlösa undantag.

Implementeringen av metoden GetHashCode() som tillhandahålls av String klassen returnerar till exempel identiska hash-koder för identiska strängvärden. Därför returnerar två String objekt samma hash-kod om de representerar samma strängvärde. Metoden använder också alla tecken i strängen för att generera rimligt slumpmässigt distribuerade utdata, även när indata grupperas i vissa intervall (till exempel kan många användare ha strängar som bara innehåller de lägre 128 ASCII-tecknen, även om en sträng kan innehålla något av de 65 535 Unicode-tecknen).

Att tillhandahålla en bra hash-funktion i en klass kan avsevärt påverka prestanda för att lägga till dessa objekt i en hash-tabell. I en hash-tabell med nycklar som ger en bra implementering av en hash-funktion tar det konstant tid att söka efter ett element (till exempel en O(1)-åtgärd). I en hash-tabell med en dålig implementering av en hash-funktion beror prestanda för en sökning på antalet objekt i hash-tabellen (till exempel en O(n)-åtgärd, där n är antalet objekt i hash-tabellen). En obehörig användare kan mata in data som ökar antalet kollisioner, vilket avsevärt kan försämra prestandan för program som är beroende av hash-tabeller under följande förhållanden:

  • När hash-funktioner ger upphov till frekventa kollisioner.

  • När en stor del av objekten i en hashtabell skapar hash-koder som är lika med eller ungefär lika med varandra.

  • När användarna anger de data som hashkoden beräknas från.

Härledda klasser som åsidosätter GetHashCode() måste också åsidosättas Equals(Object) för att garantera att två objekt som anses vara lika har samma hash-kod. Annars Hashtable kanske typen inte fungerar korrekt.

Gäller för

Se även