Object.GetHashCode Metódus

Definíció

Ez az alapértelmezett kivonatoló függvény.

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

Válaszok

Az aktuális objektum kivonatkódja.

Példák

A típusnál azonos vagy kisebb tartományú numerikus értékek kivonatkódjának kiszámításának egyik legegyszerűbb módja az, ha egyszerűen visszaadja ezt az Int32 értéket. Az alábbi példa egy ilyen implementációját mutatja be a Number struktúra esetében.

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

Egy típus gyakran több adatmezővel rendelkezik, amelyek részt vehetnek a kivonatkód létrehozásában. Kivonatkód létrehozásának egyik módja, ha ezeket a mezőket egy XOR (eXclusive OR) művelettel kombinálja, ahogyan az az alábbi példában is látható.

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

Az előző példa ugyanazt a kivonatkódot adja vissza az (n1, n2) és (n2, n1) esetében, így a kívántnál több ütközést eredményezhet. A .NET 5+-on az ajánlott megoldás a használata HashCode.Combine. Elkerüli a szimmetriaproblémát, és egy jól elosztott kivonatkódot hoz létre anélkül, hogy felesleges lenne létrehozni egy objektumot 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

Megjegyzések

A GetHashCode metódus kivonatkódot biztosít az olyan algoritmusokhoz, amelyek az objektumok egyenlőségének gyors ellenőrzését igénylik. A kivonatkód egy numerikus érték, amely egy kivonatalapú gyűjtemény objektumainak beszúrására és azonosítására szolgál, például az Dictionary<TKey,TValue> osztály, az Hashtable osztály vagy az DictionaryBase osztályból származtatott típus.

Megjegyzés:

További információ a kivonatkódok kivonattáblákban való használatáról és néhány további kivonatkód-algoritmusról: a Kivonatfüggvény bejegyzés a Wikipédiában.

Két azonos visszatérési kivonatkódú objektum, amelyek egyenlőek. A fordított érték azonban nem igaz: az egyenlő kivonatkódok nem jelentik az objektumok egyenlőségét, mivel a különböző (egyenlőtlen) objektumok azonos kivonatkódokkal rendelkezhetnek. Ezenkívül .NET nem garantálja a GetHashCode metódus alapértelmezett implementációját, és a metódus által visszaadott érték eltérhet .NET implementációk és platformok, például a 32 bites és a 64 bites platformok között. Ezért ne használja a metódus alapértelmezett implementációját kivonatolási célokra egyedi objektumazonosítóként. Ebből két következmény következik:

  • Nem szabad feltételezni, hogy az egyenlő kivonatkódok objektumegyenlőséget feltételeznek.
  • Soha ne őrizze meg vagy használja a kivonatkódot azon az alkalmazástartományon kívül, amelyben létrehozta azt, mert ugyanez az objektum az alkalmazástartományok, folyamatok és platformok kivonata lehet.

Warning

A kivonatkódok a kivonattáblán alapuló gyűjtemények hatékony beszúrására és keresésére szolgálnak. A kivonatkód nem állandó érték. Ezért:

  • Ne szerializálja a kivonatkód értékeit, és ne tárolja őket adatbázisokban.
  • Ne használja kulcsként a kivonatkódot, hogy lekérjen egy objektumot egy kulcsos gyűjteményből.
  • Ne küldjön kivonatkódokat az alkalmazástartományokban vagy folyamatokban. Bizonyos esetekben a kivonatkódok folyamatonként vagy alkalmazásonkénti tartományonként is kiszámíthatók.
  • Ha kriptográfiailag erős kivonatra van szüksége, ne használja a kivonatkódot a titkosítási kivonatoló függvény által visszaadott érték helyett. Titkosítási kivonatok esetén használjon egy osztályt, amely a System.Security.Cryptography.HashAlgorithm vagy a System.Security.Cryptography.KeyedHashAlgorithm osztályból származik.
  • Ne tesztelje a kivonatkódok egyenlőségét annak megállapításához, hogy két objektum egyenlő-e. (Az egyenlőtlen objektumok azonos hash kódokkal rendelkezhetnek.) Az egyenlőség teszteléséhez hívja meg a ReferenceEquals vagy Equals metódust.

A GetHashCode metódust származtatott típussal felül lehet bírálni. Ha a GetHashCode nincs felülírva, a referenciatípusok visszahívókódjai az alaposztály Object.GetHashCode metódusának meghívásával lesznek kiszámítva, amely egy objektum referenciája alapján állapítja meg a hash kódot; további információért lásd RuntimeHelpers.GetHashCode. Más szóval két objektum, amelynek a metódusa ReferenceEquals visszaadja true , azonos kivonatkódokkal rendelkezik. Ha az értéktípusok nem írják felül a GetHashCode-t, az alaposztály ValueType.GetHashCode metódusa reflection segítségével számítja ki a kivonatkódot a típus mezőinek értékei alapján. Más szóval azok az értéktípusok, amelyek mezői egyenlő értékekkel rendelkeznek, egyenlő kivonatkódokkal rendelkeznek. A felülírással GetHashCodekapcsolatos további információkért lásd a "Jegyzetek az öröklőknek" című szakaszt.

Warning

Ha felülbírálja a GetHashCode metódust, akkor a Equals metódust is fel kell bírálnia, és fordítva. Ha a felülírt Equals metódus két objektum egyenlőségi vizsgálatakor ad vissza true értéket, a felülírt GetHashCode metódusnak ugyanazt az értéket kell visszaadnia a két objektumhoz.

Ha egy hash-táblában kulcsként használt objektum nem nyújt hasznos implementációt GetHashCode, megadhat egy hashkód-szolgáltatót úgy, hogy egy IEqualityComparer implementációt biztosít az Hashtable osztály konstruktorának egyik túlterhelésére.

Megjegyzések az öröklőkhöz

A kivonatfüggvények segítségével gyorsan létrehozhat egy objektum értékének megfelelő számot (kivonatkódot). A kivonatfüggvények általában minden típusra jellemzőek, és az egyediség érdekében a példánymezők közül legalább egyet bemenetként kell használni. A kivonatkódokat nem szabad statikus mezők értékeivel kiszámítani.

A származtatott Objectosztályok esetében a GetHashCode metódus csak akkor delegálható az alaposztály-implementációhoz GetHashCode() , ha a származtatott osztály az egyenlőséget hivatkozási egyenlőségnek határozza meg. A referenciatípusok alapértelmezett implementációja GetHashCode() egy olyan kivonatkódot ad vissza, amely megegyezik a GetHashCode(Object) metódus által visszaadotttal. A nem módosítható referenciatípusok felülbírálhatók GetHashCode() . A nem módosítható referenciatípusok esetében általában csak a következő esetekben kell felülbírálni GetHashCode() :

  • A kivonatkódot olyan mezőkből is kiszámíthatja, amelyek nem módosíthatók; Vagy

  • Meggyőződhet arról, hogy egy mutable objektum kivonatkódja nem változik, miközben az objektum a kivonatkódjára támaszkodó gyűjteményben található.

Ellenkező esetben azt gondolhatja, hogy a mutable objektum elveszik a kivonattáblában. Ha úgy dönt, hogy felülbírál GetHashCode() egy mutable referenciatípust, a dokumentációnak egyértelművé kell tennie, hogy a típus felhasználói nem módosíthatják az objektumértékeket, miközben az objektumot kivonattáblában tárolják.

Értéktípusok esetén egy alapértelmezett kivonatkód-implementációt biztosít, GetHashCode() amely tükröződést használ. A jobb teljesítmény érdekében érdemes megfontolni a felülkondírozást.

További információkért és példákért, amelyek különböző módokon számítják ki a kivonatkódokat, tekintse meg a Példák szakaszt.

A kivonatoló függvénynek a következő tulajdonságokkal kell rendelkeznie:

  • Ha két objektum egyenlőként hasonlít össze, az GetHashCode() egyes objektumok metódusának ugyanazt az értéket kell visszaadnia. Ha azonban két objektum nem egyenlő, a GetHashCode() két objektum metódusainak nem kell eltérő értékeket visszaadnia.

  • Az GetHashCode() objektum metódusának következetesen ugyanazt a kivonatkódot kell visszaadnia, ha az objektum állapotának módosítása nem határozza meg az objektum System.Object.Equals metódusának visszatérési értékét. Vegye figyelembe, hogy ez csak az alkalmazás aktuális végrehajtására igaz, és egy másik kivonatkód is visszaadható, ha az alkalmazás újra fut.

  • A legjobb teljesítmény érdekében a kivonatfüggvénynek egyenletes eloszlást kell létrehoznia az összes bemenethez, beleértve az erősen fürtözött bemeneteket is. Ennek az a következménye, hogy az objektumállapot kis módosításainak nagy módosításokat kell eredményeznie az eredményként kapott kivonatkódon a legjobb kivonattábla-teljesítmény érdekében.

  • A kivonatfüggvények számítása olcsó.

  • A GetHashCode() metódus nem vethet ki kivételeket.

Az osztály által GetHashCode() biztosított metódus implementációja String például azonos sztringértékek azonos kivonatkódjait adja vissza. Ezért két String objektum ugyanazt a kivonatkódot adja vissza, ha ugyanazt a sztringértéket képviseli. Emellett a metódus a sztringben szereplő összes karaktert használja a véletlenszerűen elosztott kimenet létrehozásához, még akkor is, ha a bemenet bizonyos tartományokba van csoportosítva (például sok felhasználó rendelkezhet olyan sztringekkel, amelyek csak az alsó 128 ASCII-karaktert tartalmazzák, annak ellenére, hogy egy sztring a 65 535 Unicode-karakter bármelyikét tartalmazhatja).

Ha egy osztályban jó kivonatfüggvényt ad meg, az jelentősen befolyásolhatja az objektumok kivonattáblához való hozzáadásának teljesítményét. A kivonatoló függvény megfelelő megvalósítását biztosító kulcsokat tartalmazó kivonattáblákban az elem keresése állandó időt vesz igénybe (például O(1) művelet). A kivonatoló függvény gyenge implementációját tartalmazó kivonattáblákban a keresés teljesítménye a kivonattáblában lévő elemek számától függ (például egy O(n) művelettől, ahol n a kivonatoló tábla elemeinek száma. A rosszindulatú felhasználók olyan adatokat adhatnak meg, amelyek növelik az ütközések számát, ami jelentősen csökkentheti a kivonattábláktól függő alkalmazások teljesítményét a következő feltételek mellett:

  • Ha a kivonatfüggvények gyakori ütközéseket okoznak.

  • Ha egy kivonattáblában lévő objektumok nagy része olyan kivonatkódokat hoz létre, amelyek egyenlőek vagy közel azonosak egymással.

  • Amikor a felhasználók olyan adatokat adnak meg, amelyekből a kivonatkódot kiszámítják.

A felülbíráló GetHashCode() származtatott osztályokat is felül kell bírálni Equals(Object) annak biztosítása érdekében, hogy két egyenlőnek ítélt objektum ugyanazzal a kivonatkóddal rendelkezzen; ellenkező esetben előfordulhat, hogy a Hashtable típus nem működik megfelelően.

A következőre érvényes:

Lásd még