Pertanyaan Apa algoritma terbaik untuk System.Object.GetHashCode yang ditimpa?


Di .NET System.Object.GetHashCode metode digunakan di banyak tempat, di seluruh perpustakaan kelas dasar NET. Terutama ketika menemukan barang-barang dalam koleksi dengan cepat atau untuk menentukan kesetaraan. Apakah ada algoritme standar / praktik terbaik tentang cara menerapkan GetHashCode menimpa untuk kelas khusus saya jadi saya tidak menurunkan kinerja?


1216
2017-11-04 20:53


asal


Jawaban:


Saya biasanya pergi dengan sesuatu seperti implementasi yang diberikan di Josh Bloch menakjubkan  Java yang efektif. Ini cepat dan membuat hash yang cukup bagus yang tidak mungkin menyebabkan tabrakan. Pilih dua bilangan prima yang berbeda, mis. 17 dan 23, dan lakukan:

public override int GetHashCode()
{
    unchecked // Overflow is fine, just wrap
    {
        int hash = 17;
        // Suitable nullity checks etc, of course :)
        hash = hash * 23 + field1.GetHashCode();
        hash = hash * 23 + field2.GetHashCode();
        hash = hash * 23 + field3.GetHashCode();
        return hash;
    }
}

Seperti disebutkan dalam komentar, Anda mungkin menemukan lebih baik memilih prime yang besar untuk berkembang biak dengan gantinya. Ternyata 486187739 baik ... dan meskipun sebagian besar contoh yang pernah saya lihat dengan angka kecil cenderung menggunakan bilangan prima, setidaknya ada algoritma serupa di mana bilangan non-prima sering digunakan. Dalam tidak cukup-FNV Contoh kemudian, misalnya, saya telah menggunakan angka yang tampaknya berfungsi dengan baik - tetapi nilai awal bukan prima. (Konstanta penggandaan aku s prima sekalipun. Saya tidak tahu betapa pentingnya itu.)

Ini lebih baik daripada praktik umum XORing hashcodes karena dua alasan utama. Misalkan kita memiliki tipe dengan dua int ladang:

XorHash(x, x) == XorHash(y, y) == 0 for all x, y
XorHash(x, y) == XorHash(y, x) for all x, y

By the way, algoritma sebelumnya adalah yang saat ini digunakan oleh compiler C # untuk jenis anonim.

Halaman ini memberikan beberapa opsi. Saya pikir untuk sebagian besar kasus di atas adalah "cukup baik" dan sangat mudah diingat dan menjadi benar. Itu FNV alternatifnya juga sederhana, tetapi menggunakan konstanta yang berbeda dan XOR dari pada ADD sebagai operasi penggabungan. Terlihat sesuatu seperti kode di bawah ini, tetapi algoritma FNV normal beroperasi pada byte perorangan, jadi ini akan memerlukan modifikasi untuk melakukan satu iterasi per byte, bukan per 32-bit nilai hash. FNV juga dirancang untuk variabel panjang data, sedangkan cara kami menggunakannya di sini selalu untuk jumlah nilai bidang yang sama. Komentar pada jawaban ini menunjukkan bahwa kode di sini tidak benar-benar berfungsi (dalam contoh kasus diuji) sebagai pendekatan penambahan di atas.

// Note: Not quite FNV!
public override int GetHashCode()
{
    unchecked // Overflow is fine, just wrap
    {
        int hash = (int) 2166136261;
        // Suitable nullity checks etc, of course :)
        hash = (hash * 16777619) ^ field1.GetHashCode();
        hash = (hash * 16777619) ^ field2.GetHashCode();
        hash = (hash * 16777619) ^ field3.GetHashCode();
        return hash;
    }
}

Perhatikan bahwa satu hal yang harus diperhatikan adalah idealnya Anda harus mencegah status sensitif kesetaraan (dan karena itu hashcode-sensitif) berubah setelah menambahkannya ke koleksi yang bergantung pada kode hash.

Sesuai dengan dokumentasi:

Anda dapat mengganti GetHashCode untuk jenis referensi yang tidak dapat diubah. Secara umum, untuk tipe referensi yang bisa berubah, Anda harus mengganti GetHashCode hanya jika:

  • Anda dapat menghitung kode hash dari bidang yang tidak dapat diubah; atau
  • Anda dapat memastikan bahwa kode hash dari objek yang dapat berubah tidak berubah saat objek terkandung dalam koleksi yang bergantung pada kode hashnya.

1357
2017-11-04 20:56



Microsoft telah menyediakan generator HashCode generik yang bagus: Cukup salin nilai properti / bidang Anda ke jenis anonim dan hash:

new { PropA, PropB, PropC, PropD }.GetHashCode();

Ini akan berfungsi untuk sejumlah properti. Itu tidak menggunakan tinju atau sumber daya ekstra. Itu hanya menggunakan algoritma yang sudah diterapkan dalam kerangka kerja untuk jenis anonim.


302
2018-01-07 21:38



Ini adalah helper hashcode saya.
Keuntungannya adalah ia menggunakan argumen tipe generik dan karena itu tidak akan menyebabkan tinju:

public static class HashHelper
{
    public static int GetHashCode<T1, T2>(T1 arg1, T2 arg2)
    {
         unchecked
         {
             return 31 * arg1.GetHashCode() + arg2.GetHashCode();
         }
    }

    public static int GetHashCode<T1, T2, T3>(T1 arg1, T2 arg2, T3 arg3)
    {
        unchecked
        {
            int hash = arg1.GetHashCode();
            hash = 31 * hash + arg2.GetHashCode();
            return 31 * hash + arg3.GetHashCode();
        }
    }

    public static int GetHashCode<T1, T2, T3, T4>(T1 arg1, T2 arg2, T3 arg3, 
        T4 arg4)
    {
        unchecked
        {
            int hash = arg1.GetHashCode();
            hash = 31 * hash + arg2.GetHashCode();
            hash = 31 * hash + arg3.GetHashCode();
            return 31 * hash + arg4.GetHashCode();
        }
    }

    public static int GetHashCode<T>(T[] list)
    {
        unchecked
        {
            int hash = 0;
            foreach (var item in list)
            {
                hash = 31 * hash + item.GetHashCode();
            }
            return hash;
        }
    }

    public static int GetHashCode<T>(IEnumerable<T> list)
    {
        unchecked
        {
            int hash = 0;
            foreach (var item in list)
            {
                hash = 31 * hash + item.GetHashCode();
            }
            return hash;
        }
    }

    /// <summary>
    /// Gets a hashcode for a collection for that the order of items 
    /// does not matter.
    /// So {1, 2, 3} and {3, 2, 1} will get same hash code.
    /// </summary>
    public static int GetHashCodeForOrderNoMatterCollection<T>(
        IEnumerable<T> list)
    {
        unchecked
        {
            int hash = 0;
            int count = 0;
            foreach (var item in list)
            {
                hash += item.GetHashCode();
                count++;
            }
            return 31 * hash + count.GetHashCode();
        }
    }

    /// <summary>
    /// Alternative way to get a hashcode is to use a fluent 
    /// interface like this:<br />
    /// return 0.CombineHashCode(field1).CombineHashCode(field2).
    ///     CombineHashCode(field3);
    /// </summary>
    public static int CombineHashCode<T>(this int hashCode, T arg)
    {
        unchecked
        {
            return 31 * hashCode + arg.GetHashCode();   
        }
    }

Juga memiliki metode ekstensi untuk menyediakan antarmuka yang lancar, sehingga Anda dapat menggunakannya seperti ini:

public override int GetHashCode()
{
    return HashHelper.GetHashCode(Manufacturer, PartN, Quantity);
}

atau seperti ini:

public override int GetHashCode()
{
    return 0.CombineHashCode(Manufacturer)
        .CombineHashCode(PartN)
        .CombineHashCode(Quantity);
}

94
2018-04-04 18:26



Saya memiliki kelas Hashing di perpustakaan Pembantu yang saya gunakan untuk tujuan ini.

/// <summary> 
/// This is a simple hashing function from Robert Sedgwicks Hashing in C book.
/// Also, some simple optimizations to the algorithm in order to speed up
/// its hashing process have been added. from: www.partow.net
/// </summary>
/// <param name="input">array of objects, parameters combination that you need
/// to get a unique hash code for them</param>
/// <returns>Hash code</returns>
public static int RSHash(params object[] input)
{
    const int b = 378551;
    int a = 63689;
    int hash = 0;

    // If it overflows then just wrap around
    unchecked
    {
        for (int i = 0; i < input.Length; i++)
        {
            if (input[i] != null)
            {
                hash = hash * a + input[i].GetHashCode();
                a = a * b;
            }
        }
    }

    return hash;
}

Kemudian, cukup Anda dapat menggunakannya sebagai:

public override int GetHashCode()
{
    return Hashing.RSHash(_field1, _field2, _field3);
}

Saya tidak menilai kinerjanya, jadi umpan balik akan diterima.


57
2018-02-23 11:46



Berikut kelas pembantu saya menggunakan Implementasi Jon Skeet.

public static class HashCode
{
    public const int Start = 17;

    public static int Hash<T>(this int hash, T obj)
    {
        var h = EqualityComparer<T>.Default.GetHashCode(obj);
        return unchecked((hash * 31) + h);
    }
}

Pemakaian:

public override int GetHashCode()
{
    return HashCode.Start
        .Hash(_field1)
        .Hash(_field2)
        .Hash(_field3);
}

Jika Anda ingin menghindari penulisan metode ekstensi untuk System.Int32:

public struct HashCode
{
    private readonly int _value;

    public HashCode(int value) => _value = value;

    public static HashCode Start { get; } = new HashCode(17);

    public static implicit operator int(HashCode hash) => hash._value;

    public HashCode Hash<T>(T obj)
    {
        var h = EqualityComparer<T>.Default.GetHashCode(obj);
        return unchecked(new HashCode((_value * 31) + h));
    }

    public override int GetHashCode() => _value;
}

Masih generik, masih menghindari alokasi heap dan digunakan dengan cara yang sama:

public override int GetHashCode()
{
    // This time `HashCode.Start` is not an `Int32`, it's a `HashCode` instance.
    // And the result is implicitly converted to `Int32`.
    return HashCode.Start
        .Hash(_field1)
        .Hash(_field2)     
        .Hash(_field3);
}

Perbarui setelah komentar Martin:

obj != null disebabkan tinju jadi saya beralih ke pembanding default.

  • Lihat jawaban ini mengenai kinerja pembanding standar.
  • Lihat pertanyaan ini untuk diskusi tentang kode hash dari nilai nol.

Edit (Mei 2018):

EqualityComparer<T>.Default pengambil sekarang menjadi JIT intrinsik - tarik permintaan disebutkan oleh Stephen Toub di posting blog ini.


49
2017-09-04 12:32



Dalam banyak kasus di mana Equals () membandingkan beberapa field, itu tidak terlalu penting jika hash GetHash (Anda) pada satu bidang atau banyak. Anda hanya perlu memastikan bahwa menghitung hash benar-benar murah (Tanpa alokasi, silakan) dan cepat (Tidak ada perhitungan yang berat dan tentu saja tidak ada koneksi database) dan memberikan distribusi yang baik.

Pengangkatan berat harus menjadi bagian dari metode Setara (); hash harus menjadi operasi yang sangat murah untuk mengaktifkan Panggilan Setara () pada sesedikit mungkin item.

Dan satu tip terakhir: Jangan mengandalkan GetHashCode () yang stabil selama beberapa aplikasi berjalan. Banyak. Bersih jenis tidak menjamin kode hash mereka untuk tetap sama setelah me-restart, jadi Anda hanya harus menggunakan nilai GetHashCode () untuk dalam struktur data memori.


26
2018-02-23 11:55



Sampai baru-baru ini jawaban saya akan sangat dekat dengan Jon Skeet di sini. Namun, saya baru-baru ini memulai proyek yang menggunakan tabel hash kekuatan-dari-dua, yaitu tabel hash di mana ukuran tabel internal adalah 8, 16, 32, dll. Ada alasan bagus untuk mendukung ukuran bilangan prima, tetapi ada adalah beberapa keuntungan untuk kekuatan-dari-dua ukuran juga.

Dan itu cukup banyak disedot. Jadi setelah sedikit eksperimen dan penelitian saya mulai mem-hashing ulang hash saya dengan yang berikut:

public static int ReHash(int source)
{
  unchecked
  {
    ulong c = 0xDEADBEEFDEADBEEF + (ulong)source;
    ulong d = 0xE2ADBEEFDEADBEEF ^ c;
    ulong a = d += c = c << 15 | c >> -15;
    ulong b = a += d = d << 52 | d >> -52;
    c ^= b += a = a << 26 | a >> -26;
    d ^= c += b = b << 51 | b >> -51;
    a ^= d += c = c << 28 | c >> -28;
    b ^= a += d = d << 9 | d >> -9;
    c ^= b += a = a << 47 | a >> -47;
    d ^= c += b << 54 | b >> -54;
    a ^= d += c << 32 | c >> 32;
    a += d << 25 | d >> -25;
    return (int)(a >> 1);
  }
}

Dan kemudian tabel hash kekuatan-dua saya tidak menyedot lagi.

Ini mengganggu saya, karena hal di atas seharusnya tidak berfungsi. Atau lebih tepatnya, seharusnya tidak berfungsi kecuali yang asli GetHashCode() miskin dengan cara yang sangat khusus.

Pencampuran ulang hashcode tidak dapat meningkatkan hashcode yang hebat, karena satu-satunya efek yang mungkin adalah kami memperkenalkan beberapa tabrakan lagi.

Mencampur ulang kode hash tidak dapat memperbaiki kode hash yang buruk, karena satu-satunya efek yang mungkin adalah kita mengubah, mis. sejumlah besar tabrakan pada nilai 53 ke sejumlah besar nilai 18,3487,291.

Mencampur ulang kode hash hanya dapat memperbaiki kode hash yang setidaknya berhasil dalam menghindari tabrakan mutlak di seluruh rentangnya (232 nilai yang mungkin) tetapi buruk dalam menghindari tabrakan ketika modul turun untuk penggunaan aktual dalam tabel hash. Sementara modulo sederhana dari kekuatan-dari-dua meja membuat ini lebih jelas, itu juga memiliki efek negatif dengan tabel bilangan prima yang lebih umum, yang tidak begitu jelas (kerja ekstra dalam pengulangan akan lebih besar daripada manfaatnya). , tetapi manfaatnya tetap ada di sana).

Sunting: Saya juga menggunakan pengalamatan terbuka, yang juga meningkatkan kepekaan terhadap tabrakan, mungkin lebih dari kenyataan itu adalah kekuatan-dua.

Dan baik, itu mengganggu seberapa banyak string.GetHashCode() implementasi dalam .BERSIH (atau belajar sini) dapat ditingkatkan dengan cara ini (pada urutan pengujian berjalan sekitar 20-30 kali lebih cepat karena lebih sedikit tabrakan) dan lebih mengganggu seberapa banyak kode hash saya dapat ditingkatkan (lebih dari itu).

Semua implementasi GetHashCode () yang saya kode di masa lalu, dan memang digunakan sebagai dasar jawaban di situs ini, jauh lebih buruk daripada yang saya ketahui. Sebagian besar waktu itu "cukup baik" untuk banyak penggunaan, tetapi saya menginginkan sesuatu yang lebih baik.

Jadi saya menempatkan proyek itu pada satu sisi (itu adalah proyek hewan peliharaan) dan mulai mencari cara menghasilkan kode hash yang bagus dan terdistribusi dengan baik di .NET dengan cepat.

Pada akhirnya saya memutuskan untuk melakukan porting SpookyHash ke .NET. Memang kode di atas adalah versi jalan cepat menggunakan SpookyHash untuk menghasilkan output 32-bit dari input 32-bit.

Sekarang, SpookyHash bukanlah potongan kode yang bagus dan cepat diingat. Port saya bahkan kurang begitu karena saya banyak memasukkannya untuk kecepatan yang lebih baik *. Tapi itulah gunanya kembali kode.

Lalu saya letakkan bahwa proyek ke satu sisi, karena sama seperti proyek asli telah menghasilkan pertanyaan tentang bagaimana menghasilkan kode hash yang lebih baik, sehingga proyek tersebut menghasilkan pertanyaan tentang bagaimana menghasilkan memcpy .NET yang lebih baik.

Lalu saya kembali, dan menghasilkan banyak kelebihan untuk dengan mudah memberi makan hampir semua jenis asli (kecuali decimal†) menjadi kode hash.

Ini cepat, yang mana Bob Jenkins layak mendapat kredit karena kode aslinya yang saya pilah lebih cepat, terutama pada mesin 64-bit yang dioptimalkan algoritma untuk ‡.

Kode lengkap dapat dilihat di https://bitbucket.org/JonHanna/spookilysharp/src tetapi pertimbangkan bahwa kode di atas adalah versi yang disederhanakan.

Namun, karena sekarang sudah ditulis, seseorang dapat menggunakannya dengan lebih mudah:

public override int GetHashCode()
{
  var hash = new SpookyHash();
  hash.Update(field1);
  hash.Update(field2);
  hash.Update(field3);
  return hash.Final().GetHashCode();
}

Ini juga membutuhkan nilai benih, jadi jika Anda perlu menangani masukan yang tidak tepercaya dan ingin melindungi dari serangan Hash DoS, Anda dapat menetapkan benih berdasarkan waktu aktif atau yang serupa, dan membuat hasilnya tidak dapat diprediksi oleh penyerang:

private static long hashSeed0 = Environment.TickCount;
private static long hashSeed1 = DateTime.Now.Ticks;
public override int GetHashCode()
{
  //produce different hashes ever time this application is restarted
  //but remain consistent in each run, so attackers have a harder time
  //DoSing the hash tables.
  var hash = new SpookyHash(hashSeed0, hashSeed1);
  hash.Update(field1);
  hash.Update(field2);
  hash.Update(field3);
  return hash.Final().GetHashCode();
}

* Kejutan besar dalam hal ini adalah metode perputaran tangan yang mengembalikan metode (x << n) | (x >> -n) memperbaiki hal-hal. Saya akan yakin bahwa jitter akan mencondongkan itu untuk saya, tetapi profil menunjukkan sebaliknya.

decimal tidak asli dari. NET perspektif meskipun dari C #. Masalahnya adalah masalah itu sendiri GetHashCode() memperlakukan presisi sebagai signifikan sementara miliknya sendiri Equals() tidak. Keduanya adalah pilihan yang valid, tetapi tidak tercampur seperti itu. Dalam menerapkan versi Anda sendiri, Anda harus memilih untuk melakukan salah satu, atau yang lain, tetapi saya tidak tahu yang Anda inginkan.

‡ Dengan cara perbandingan. Jika digunakan pada string, SpookyHash pada 64 bit jauh lebih cepat daripada string.GetHashCode() pada 32 bit yang sedikit lebih cepat dari string.GetHashCode() pada 64 bit, yang jauh lebih cepat daripada SpookyHash pada 32 bit, meskipun masih cukup cepat untuk menjadi pilihan yang masuk akal.


18
2018-01-14 14:15



Ini bagus:

/// <summary>
/// Helper class for generating hash codes suitable 
/// for use in hashing algorithms and data structures like a hash table. 
/// </summary>
public static class HashCodeHelper
{
    private static int GetHashCodeInternal(int key1, int key2)
    {
        unchecked
        {
           var num = 0x7e53a269;
           num = (-1521134295 * num) + key1;
           num += (num << 10);
           num ^= (num >> 6);

           num = ((-1521134295 * num) + key2);
           num += (num << 10);
           num ^= (num >> 6);

           return num;
        }
    }

    /// <summary>
    /// Returns a hash code for the specified objects
    /// </summary>
    /// <param name="arr">An array of objects used for generating the 
    /// hash code.</param>
    /// <returns>
    /// A hash code, suitable for use in hashing algorithms and data 
    /// structures like a hash table. 
    /// </returns>
    public static int GetHashCode(params object[] arr)
    {
        int hash = 0;
        foreach (var item in arr)
            hash = GetHashCodeInternal(hash, item.GetHashCode());
        return hash;
    }

    /// <summary>
    /// Returns a hash code for the specified objects
    /// </summary>
    /// <param name="obj1">The first object.</param>
    /// <param name="obj2">The second object.</param>
    /// <param name="obj3">The third object.</param>
    /// <param name="obj4">The fourth object.</param>
    /// <returns>
    /// A hash code, suitable for use in hashing algorithms and
    /// data structures like a hash table.
    /// </returns>
    public static int GetHashCode<T1, T2, T3, T4>(T1 obj1, T2 obj2, T3 obj3,
        T4 obj4)
    {
        return GetHashCode(obj1, GetHashCode(obj2, obj3, obj4));
    }

    /// <summary>
    /// Returns a hash code for the specified objects
    /// </summary>
    /// <param name="obj1">The first object.</param>
    /// <param name="obj2">The second object.</param>
    /// <param name="obj3">The third object.</param>
    /// <returns>
    /// A hash code, suitable for use in hashing algorithms and data 
    /// structures like a hash table. 
    /// </returns>
    public static int GetHashCode<T1, T2, T3>(T1 obj1, T2 obj2, T3 obj3)
    {
        return GetHashCode(obj1, GetHashCode(obj2, obj3));
    }

    /// <summary>
    /// Returns a hash code for the specified objects
    /// </summary>
    /// <param name="obj1">The first object.</param>
    /// <param name="obj2">The second object.</param>
    /// <returns>
    /// A hash code, suitable for use in hashing algorithms and data 
    /// structures like a hash table. 
    /// </returns>
    public static int GetHashCode<T1, T2>(T1 obj1, T2 obj2)
    {
        return GetHashCodeInternal(obj1.GetHashCode(), obj2.GetHashCode());
    }
}

Dan inilah cara menggunakannya:

private struct Key
{
    private Type _type;
    private string _field;

    public Type Type { get { return _type; } }
    public string Field { get { return _field; } }

    public Key(Type type, string field)
    {
        _type = type;
        _field = field;
    }

    public override int GetHashCode()
    {
        return HashCodeHelper.GetHashCode(_field, _type);
    }

    public override bool Equals(object obj)
    {
        if (!(obj is Key))
            return false;
        var tf = (Key)obj;
        return tf._field.Equals(_field) && tf._type.Equals(_type);
    }
}

12
2017-10-07 10:51



Inilah pendekatan saya yang sederhana. Saya menggunakan pola builder klasik untuk ini. Ini adalah typesafe (tidak ada tinju / unboxing) dan juga compatbile dengan .NET 2.0 (tidak ada metode penyuluhan, dll.).

Ini digunakan seperti ini:

public override int GetHashCode()
{
    HashBuilder b = new HashBuilder();
    b.AddItems(this.member1, this.member2, this.member3);
    return b.Result;
} 

Dan di sini adalah kelas pembangun acutal:

internal class HashBuilder
{
    private const int Prime1 = 17;
    private const int Prime2 = 23;
    private int result = Prime1;

    public HashBuilder()
    {
    }

    public HashBuilder(int startHash)
    {
        this.result = startHash;
    }

    public int Result
    {
        get
        {
            return this.result;
        }
    }

    public void AddItem<T>(T item)
    {
        unchecked
        {
            this.result = this.result * Prime2 + item.GetHashCode();
        }
    }

    public void AddItems<T1, T2>(T1 item1, T2 item2)
    {
        this.AddItem(item1);
        this.AddItem(item2);
    }

    public void AddItems<T1, T2, T3>(T1 item1, T2 item2, T3 item3)
    {
        this.AddItem(item1);
        this.AddItem(item2);
        this.AddItem(item3);
    }

    public void AddItems<T1, T2, T3, T4>(T1 item1, T2 item2, T3 item3, 
        T4 item4)
    {
        this.AddItem(item1);
        this.AddItem(item2);
        this.AddItem(item3);
        this.AddItem(item4);
    }

    public void AddItems<T1, T2, T3, T4, T5>(T1 item1, T2 item2, T3 item3, 
        T4 item4, T5 item5)
    {
        this.AddItem(item1);
        this.AddItem(item2);
        this.AddItem(item3);
        this.AddItem(item4);
        this.AddItem(item5);
    }        

    public void AddItems<T>(params T[] items)
    {
        foreach (T item in items)
        {
            this.AddItem(item);
        }
    }
}

8
2018-03-22 12:15



Berikut ini adalah implementasi fasih dari algoritma yang diposting di atas oleh Jon Skeet, tetapi tidak termasuk alokasi atau operasi tinju:

public static class Hash
{
    public const int Base = 17;

    public static int HashObject(this int hash, object obj)
    {
        unchecked { return hash * 23 + (obj == null ? 0 : obj.GetHashCode()); }
    }

    public static int HashValue<T>(this int hash, T value)
        where T : struct
    {
        unchecked { return hash * 23 + value.GetHashCode(); }
    }
}

Pemakaian:

public class MyType<T>
{
    public string Name { get; set; }

    public string Description { get; set; }

    public int Value { get; set; }

    public IEnumerable<T> Children { get; set; }

    public override int GetHashCode()
    {
        return Hash.Base
            .HashObject(this.Name)
            .HashObject(this.Description)
            .HashValue(this.Value)
            .HashObject(this.Children);
    }
}

Compiler akan memastikan HashValue tidak dipanggil dengan kelas karena kendala tipe generik. Tetapi tidak ada dukungan kompilator untuk HashObject sejak menambahkan argumen umum juga menambahkan operasi tinju.


8
2018-01-20 23:41