Aracılığıyla paylaş


System.Object.GetHashCode yöntemi

Bu makale, bu API'nin başvuru belgelerine ek açıklamalar sağlar.

GetHashCode yöntemi, nesne eşitliği için hızlı denetimler gerektiren algoritmalar için bir karma kod sağlar. Karma kod, Dictionary<TKey,TValue> sınıfı, Hashtable sınıfı veya DictionaryBase sınıfından türetilmiş bir tür gibi, karma tabanlı bir koleksiyona nesne eklemek ve tanımlamak için kullanılan sayısal bir değerdir.

Uyarı

Karma kodların karma tablolarda nasıl kullanıldığı hakkında bilgi edinmek ve bazı ek karma kod algoritmaları için Wikipedia'daki Karma İşlevi girdisine bakın.

Birbirine eşit olan iki nesne, eşit karma kodları döndürür. Ancak, tersi doğru değildir: eşit karma kodları nesne eşitliği anlamına gelmez, çünkü farklı (eşit olmayan) nesneler aynı karma kodlara sahip olabilir. Ayrıca. .NET, yöntemin varsayılan uygulamasını GetHashCode garanti etmez ve bu yöntemin döndürdüğü değer, .NET Framework ve .NET Core'un farklı sürümleri gibi .NET uygulamaları ile 32 bit ve 64 bit platformlar gibi platformlar arasında farklılık gösterebilir. Bu nedenlerden dolayı, karma oluşturma amacıyla bu yöntemin varsayılan uygulamasını benzersiz bir nesne tanımlayıcısı olarak kullanmayın. Bundan iki sonuç çıkar:

  • Eşit karma kodlarının nesne eşitliği anlamına geldiğini varsaymamalısınız.
  • Hash kodunu oluşturulduğu uygulama etki alanı dışında asla kalıcı hale getirmemeli veya kullanmamalısınız, çünkü aynı nesne uygulama etki alanları, süreçler ve platformlar arasında farklı sonuçlar verebilir.

Uyarı

Karma kod, karma tabloyu temel alan koleksiyonlarda verimli ekleme ve arama için tasarlanmıştır. Karma kod kalıcı bir değer değildir. Bu nedenle:

  • Karma kod değerlerini seri hale getirmeyin veya veritabanlarında depolamayın.
  • Anahtarlı bir koleksiyondan nesne almak için karma kodu anahtar olarak kullanmayın.
  • Karma kodları uygulama etki alanları veya işlemler arasında göndermeyin. Bazı durumlarda, hash kodları her bir işlem veya uygulama etki alanı bazında hesaplanabilir.
  • Şifreleme açısından güçlü bir karmaya ihtiyacınız varsa, şifreleme karma işlevi tarafından döndürülen bir değer yerine karma kodu kullanmayın. Şifreleme karmaları için, System.Security.Cryptography.HashAlgorithm veya System.Security.Cryptography.KeyedHashAlgorithm sınıfından türetilmiş bir sınıf kullanın.
  • İki nesnenin eşit olup olmadığını belirlemek için karma kodların eşitliğini test etmeyin. (False olan nesneler aynı hash kodlarına sahip olabilir.) Eşitliği test etmek için ReferenceEquals veya Equals yöntemini çağırın.

Yöntem GetHashCode türetilmiş bir tür tarafından geçersiz kılınabilir. GetHashCode Geçersiz kılınmazsa, referans türleri için hash kodları, bir nesnenin referansına göre bir hash kodu hesaplayan temel sınıfın Object.GetHashCode yöntemi çağrılarak hesaplanır; daha fazla bilgi için bkz. RuntimeHelpers.GetHashCode. Başka bir deyişle, yönteminin döndürdüğü ReferenceEqualstrue iki nesne aynı karma kodlara sahiptir. Değer türleri GetHashCode ve ValueType.GetHashCode yöntemini geçersiz kılmıyorsa, temel sınıfın yöntemi, türün alanlarının değerlerine dayanarak karma kodunu hesaplamak için yansıma kullanır. Başka bir deyişle, alanları eşit değerlere sahip olan değer türleri eşit karma kodlara sahiptir. geçersiz kılma GetHashCodehakkında daha fazla bilgi için "Devralanlara Notlar" bölümüne bakın.

Uyarı

Eğer GetHashCode yöntemini geçersiz kılarsanız, Equals yöntemini de geçersiz kılmanız gerekir ve bunun tersi de geçerlidir. Geçersiz kılınan Equals yönteminiz iki nesne eşitlik için test edildiğinde döndürülüyorsa true , geçersiz kılınan GetHashCode yönteminiz iki nesne için aynı değeri döndürmelidir.

Eğer karma tablosunda anahtar olarak kullanılan bir nesne, GetHashCode'ın kullanışlı bir uygulamasını sağlamıyorsa, sınıf oluşturucusunun IEqualityComparer aşırı yüklemelerinden birine bir Hashtable hash kodu sağlayıcısı uygulaması vererek belirtebilirsiniz.

Windows Çalışma Zamanı notları

Windows Çalışma Zamanı'ndaki bir sınıfta GetHashCode metodunu çağırdığınızda, GetHashCode sınıflarını geçersiz kılmayanlar için varsayılan davranışı sağlar. Bu, .NET'in Windows Çalışma Zamanı için sağladığı desteğin bir parçasıdır (bkz. Windows Mağazası Uygulamaları ve Windows Çalışma Zamanı için .NET Desteği). Windows Çalışma Zamanı'ndaki sınıflar Object'yı devralmaz ve şu anda bir GetHashCode'yi uygulamaz. Ancak, C# veya Visual Basic kodunuzda kullandığınızda, ToString, Equals(Object), ve GetHashCode yöntemlerine sahip gibi görünürler ve .NET Framework bu yöntemler üzerinde varsayılan davranışı sağlar.

Uyarı

C# veya Visual Basic ile yazılan Windows Çalışma Zamanı sınıfları yöntemini geçersiz kılabilir GetHashCode .

Örnekler

Sayısal bir değer için, Int32 türüyle aynı veya daha küçük bir aralığı olan bir karma kodu hesaplamanın en basit yollarından biri sadece bu değeri iade etmektir. Aşağıdaki örnekte bir Number yapısının böyle bir uygulaması gösterilmektedir.

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

Bir tür genellikle karma kodunun oluşturulmasına katılan birden çok veri alanına sahiptir. Karma kod oluşturmanın bir yolu, aşağıdaki örnekte gösterildiği gibi bu alanları bir XOR (eXclusive OR) işlem kullanarak birleştirmektir.

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 $"{pt.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

Önceki örnek aynı karma kodu (n1, n2) ve (n2, n1) döndürür ve bu nedenle istenenden daha fazla çakışma oluşturabilir. Bu gibi durumlarda karma kodların aynı olmaması için bir dizi çözüm kullanılabilir. Bunlardan biri, her alanın sırasını yansıtan bir Tuple nesnenin karma kodunu döndürmektir. Aşağıdaki örnek, Tuple<T1,T2> sınıfını kullanan olası bir uygulamayı göstermektedir. Ancak, bir Tuple nesnenin örneğini oluşturmanın performans ek yükünün, karma tablolarda çok sayıda nesne depolayan bir uygulamanın genel performansını önemli ölçüde etkileyeebileceğini unutmayın.

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 Tuple.Create(x, y).GetHashCode();
    }
}

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 the following output:
//       173
//       269
[<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).GetHashCode()

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

let pt2 = Point(8, 5)
printfn $"{pt2.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

İkinci bir alternatif çözüm, ardışık alanların karma kodlarını iki veya daha fazla bit sola kaydırarak tek tek karma kodların ağırlığını belirlemeyi içerir. En uygun şekilde, 31 bitten öteye kaydırılan bitler atılmak yerine geri döndürülmelidir. Bitler, C# ve Visual Basic'teki sola kaydırma operatörleri tarafından atıldığından, bunun için aşağıdaki gibi bir sola kaydırma ve sarma yöntemi oluşturulması gerekir.

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);
}
let shiftAndWrap (value: int) positions =
    let positions = positions &&& 0x1F

    // Save the existing bit pattern, but interpret it as an unsigned integer.
    let number = BitConverter.ToUInt32(BitConverter.GetBytes value, 0)
    // Preserve the bits to be discarded.
    let wrapped = number >>> (32 - positions)
    // Shift and wrap the discarded bits.
    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

Aşağıdaki örnek, önceki örneklerde kullanılan yapının karma kodunu Point hesaplamak için bu shift-and-wrap yöntemini kullanır.

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 Example2
{
   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
open System

[<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 this.GetHashCode() =
        this.ShiftAndWrap(x.GetHashCode(), 2) ^^^ y.GetHashCode()

    member _.ShiftAndWrap(value, positions) =
        let positions = positions &&& 0x1F

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

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

let pt2 = Point(8, 5)
printfn $"{pt2.GetHashCode()}"
// The example displays the following output:
//       28
//       37
Public Structure Point5
    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 Point5 Then Return False

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

        pt = New Point5(8, 5)
        Console.WriteLine(pt.GetHashCode())
    End Sub
End Module
' The example displays the following output:
'       28
'       37