Üstünlüklü System.Object.GetHashCode üçün ən yaxşı alqoritm nədir?

.NET System.Object.GetHashCode metodu, .NET əsas sinifinin bütün kitabxanalarında bir çox yerlərdə istifadə olunur. Xüsusilə kolleksiyada maddələr axtaranda və ya bərabərliyi müəyyən edərkən. Mənim istifadəçi dərsləri üçün GetHashCode tətbiq etmək üçün standart / yaxşı bir təcrübə varmı?

1289
04 нояб. bitonkarı 04 Noyabr. 2008-11-04 23:53 '08 at 11:53 2008-11-04 23:53
@ 18 cavab

Mən adətən Josh Blokun inanılmaz Effektiv Java proqramında verilmiş bir tətbiq kimi bir şey edirəm. Bu sürətli və çarpışmalara səbəb olmayacaq çox yaxşı bir xaş yaradılır. İki fərqli baş ədədləri, məsələn, 17 və 23 seçin və yerinə yetirin:

 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; } } 

Şərhdə qeyd edildiyi kimi, çarpma üçün böyük bir sayını seçmədən daha yaxşı ola bilərsiniz. Şübhəsiz ki, 486187739 yaxşıdır ... və kiçik nömrələrlə gördüyüm nümunələrin əksəriyyəti sadə nömrələrdən istifadə etməyə məcbur olsa da, ən azı qeyri-sadə nömrələri istifadə edən oxşar alqoritmlər var. Məsələn, kifayət qədər FNV olmayan nümunədə daha sonra yaxşı işləyən ədədlərdən istifadə etdim, amma ilk dəyər sadə deyil. (Çarpma sabitliyi sadə, baxmayaraq ki, bunun nə qədər əhəmiyyətli olduğunu bilmirəm.)

Bu iki əsas səbəbdən XOR hash kodlarının istifadəsinin adi tətbiqindən daha yaxşıdır. İki int alanları olan bir növü var:

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

Yeri gəlmişkən, əvvəlki alqoritm hazırda C # tərtibçisi tərəfindən anonim növlər üçün istifadə olunur.

Bu səhifə bir neçə variant verir. Hesab edirəm ki, əksər hallarda yuxarıda "kifayət qədər yaxşıdır" və yadda saxlamaq və düzgün başa düşmək olduqca asandır. FNV-yə alternativ də sadədir, lakin qoşulma əməliyyatı kimi ADD yerinə fərqli sabitləri və XOR istifadə edir. Aşağıdakı kimi bir şeyə baxırsınız, ancaq FNV adi alqoritm fərdi baytlarla işləyir, buna görə 32 bit hash dəyərinin yerinə bayt başına bir təkrarlama etmək tələb olunur. FNV də dəyişən məlumat uzunluqları üçün nəzərdə tutulmuşdur, buna baxmayaraq biz həmişə həmin ədədi dəyərlərin eyni sayından istifadə edirik. Bu cavabı şərhlərə əsasən, burada kod burada həqiqətən eyni şəkildə işləmir (test edilmiş misalda) yuxarıda təsvir edilən əlavə metod kimi.

 // 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; } } 

Xeyir yadda saxlamalıyıq ki, ideal olaraq, hash kodundan asılı olan bir kolleksiyaya əlavə etdikdən sonra bərabərliyə həssas olan dövlətin dəyişməsini (həssaslıq koduna həssas) önləməlisiniz.

Sənədlərə görə:

GetHashCode'yu dəyişməz istinad növləri üçün ləğv edə bilərsiniz. Ümumiyyətlə, dəyişdirilə bilən istinad növləri üçün GetHashCode'u yalnız əgər:

  • Hash kodunu dəyişdirə bilməyən sahələrdən hesablaya bilərsiniz; və ya
  • Nesne öz hash koduna əsaslanan bir kolleksiyada olduğu müddətdə dəyişən obyektin hash kodunun dəyişməyəcəyini təmin edə bilərsiniz.
1427
04 нояб. Cavab Jon Skeet tərəfindən 04 Noyabr. 2008-11-04 23:56 '08 at 23:56 2008-11-04 23:56

Anonim tip

Microsoft artıq yaxşı bir HashCode generator təmin edir: yalnız anonim bir növü və hash daxil əmlak / sahəsində dəyərləri kopyalayın:

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

Bu hər hansı bir sıra xüsusiyyət üçün işləyəcək. Boks istifadə etmir. Anonim tiplərdə artıq tətbiq olunan bir alqoritm istifadə edir.

border=0

ValueTuple - C # 7 üçün yeniləmə

Şərhlərdə @ kaktuaroyun qeyd edildiyi kimi, dəyərlərdən bir dəstə istifadə edə bilərsiniz. Bu, bir neçə tuş vuruşunu saxlayır və daha da önemlisi, yığın üstündə (zibil olmadan) çalışır:

 (PropA, PropB, PropC, PropD).GetHashCode(); 

(Qeyd: anonim növlərdən istifadə edən orijinal texnika kompüter tərəfindən optimallaşdırıla bilsə də anonim növlər dərs kimi tətbiq olunduğundan yığındakı bir obyekt yaratmaq kimi görünür, çünki bu parametrləri yoxlamaq maraqlıdır, amma tuple seçimi daha yüksək olmalıdır .)

327
08 янв. Rick Love tərəfindən Jan 08 tərəfindən verilən cavab 2011-01-08 00:38 '11 at 0:38 2011-01-08 00:38

İşdə köməkçi hash kodum.
Üstünlük, ümumi tipli arqumentlərdən istifadə edir və bu səbəbdən qutu deyilmir:

 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(); } } 

Bu da bir pulsuz interfeys təmin etmək üçün bir uzantı metoduna malikdir, belə ki, aşağıdakı kimi istifadə edə bilərsiniz:

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

və ya belədir:

 public override int GetHashCode() { return 0.CombineHashCode(Manufacturer) .CombineHashCode(PartN) .CombineHashCode(Quantity); } 
97
04 апр. cavab gecə-gündüz 04 apr tərəfindən verilir . 2010-04-04 21:26 '10 at 21:26 2010-04-04 21:26

Mən bu məqsədlə istifadə edən Helper kitabxanasında Hashing sinfi var.

 /// <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; } 

Sonra belə istifadə edə bilərsiniz:

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

Mən onun fəaliyyətini yüksək qiymətləndirməmişəm, buna görə hər hansı bir rəy var.

59
23 февр. Cavab 23 fevralda Vahid Şalalı tərəfindən verilir. 2009-02-23 14:46 '09 da 14:46, 2009-02-23 14:46

John Skit'in tətbiqini istifadə edərək köməkçi sinifim .

 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); } } 

Ərizə:

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

System.Int32 üçün bir uzantısı metodunun yazılmasını qarşısını almaq istəyirsinizsə:

 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; } 

Hələ də yaygındır, yığın paylamasını qarşısını alır və eyni şəkildə istifadə edir:

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

Martinin şərhindən sonra yeniləmə:

obj != null boksa səbəb oldu, mən də default müqayisə etdim.

  • Əksinə komparator performansı üçün bu cavaba baxın.
  • Sıfır dəyərli hash kodlarının müzakirəsi üçün bu suallara baxın.

Change (May 2018):

EqualityComparer<T>.Default getter indi bir daxili JIT - EqualityComparer<T>.Default üçün müraciət EqualityComparer<T>.Default bu blogda Stephen Tub tərəfindən qeyd edilir.

51
04 сент. Şafak Gür tərəfindən verilmiş cavab 04 Sep. 2013-09-04 15:32 '13 at 15:32 2013-09-04 15:32

Çox hallarda, bərabər () bir neçə sahəni müqayisə edəndə, bir sahədə və ya bir çoxunda GetHash () xeyləri varmı istəmir. Yalnız hash hesabı həqiqətən ucuz ( No ayırma , xahiş edirik) və sürətli ( heç bir ağır hesablamalar və əlbəttə, verilənlər bazası ilə heç bir əlaqələr) və yaxşı bir paylanması təmin edir əmin etmək lazımdır.

Ağır qaldırmaq Equals () metodunun bir hissəsi olmalıdır; haşqın çox ucuz bir əməliyyat olması, Equals () mümkün qədər az element çağırmasına imkan verməlidir.

Və son bir ipucu: GetHashCode (), birdən çox proqramı işləyərkən stabil olan etibar etməyin . Bir çox. Net növləri yenidən başladıqdan sonra onların hash kodlarının dəyişməyəcəyinə zəmanət vermir, beləliklə yaddaş məlumatları strukturları üçün GetHashCode () dəyərindən istifadə etməlisiniz.

27
23 февр. Bert Huijben tərəfindən 23 Fevralda verilən cavab 2009-02-23 14:55 '09 at 14:55 'da 2009-02-23 14:55

Son vaxtlara qədər mənim cavabım John Skit-a çox yaxın idi. Bununla yanaşı, yaxınlarda mən güc xüsusiyyətləri olan iki masa masası, yəni daxili masanın ölçüsü 8, 16, 32 və s. Sadə ölçüləri seçmək üçün yaxşı bir səbəb var - n, lakin iki ölçü üçün bir sıra üstünlüklər var.

Və olduqca çox sucked. Buna görə, bir neçə təcrübə və tədqiqatdan sonra, mənim həsirlərimi aşağıdakılarla təkrar etməyə başladı:

 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); } } 

Və sonra mənim güc masam ilə hash artıq sucked.

Bu məni narahat etdi, çünki yuxarıda işləməmişdi. Daha doğrusu, orijinal GetHashCode() çox pis olduqda GetHashCode() .

Hash kodunun yenidən qarışdırılması böyük hash kodunu inkişaf etdirə bilməz, çünki mümkün olan bir neçə təsir daha bir neçə toqquşma ilə nəticələnir.

Hash kodunu təkrar qarışdırmaq, dəhşətli hash kodunu düzəldə bilməz, çünki mümkün olan bir təsir biz, məsələn, 53 dəyərinə çox sayda çarpışmanın çox sayda 18,3487,291 dəyərinə dəyişdiyidir.

Hash kodunu təkrar qarışdırmaq yalnız bütün aralıda mütləq toqquşmalardan (2 32 mümkün dəyər) kifayət qədər yaxşı bir şəkildə qaçmaq üçün yaxşı olan hash kodunu düzəldə bilər, lakin hash masasında faktiki istifadəyə görə modul istifadə ilə toqquşmanın qarşısını almaq üçün pisdir. Sadə modulo "iki qüvvət" cədvəli daha aydın olsa da, daha geniş yayılmış bazlı masalar - sadəcə olaraq açıq olmayan (təkrar təzələmək üçün əlavə iş faydaları üstün tutacaq, orada bərabər olacaq).

Düzəlişlər: Mən də açıq addım istifadə etdim ki, bu da çarpışma həssaslığını artıra bilər, bəlkə də, bu, ikinin gücündən daha çoxdur.

Yaxşı olardı ki, string.GetHashCode() tətbiqi ola bilərdi (və ya burada tədqiq) daha az çarpışmalara görə 20-30 dəfə sürətli testlər üçün və daha çox Haşim kodlarımın bir çoxunu narahat etməli (daha çox).

GetHashCode () keçmişdə kodlanmış və həqiqətən bu saytda cavablar üçün əsas kimi istifadə edilən bütün tətbiqlər məndən daha pis idi . Çox hallarda, ən çox tətbiq üçün "kifayət qədər yaxşı" idi, amma yaxşı bir şey istədim.

Buna görə də, bu layihəni bir tərəfə qoyduğum (bir pet layihəsi idi) və .NET-də yaxşı, yaxşı paylanmış hash kodunun necə tez qurulacağına baxmağa başladı.

Nəticədə mən SpookyHash limanına .NET limanına qərar verdim. Həqiqətən, yuxarıda göstərilən kod, 32-bit girişdən 32 bitlik çıxış yaratmaq üçün SpookyHash istifadə etmək üçün sürətli bir şəkildə bir versiyasıdır.

İndi SpookyHash bir kod parçasını çox yaxşı xatırlayır. Mənim liman daha kiçikdir, çünki əlimlə daha yaxşı sürət üçün çox əl qoydu *. Kod üçün təkrar istifadə nədir?

Sonra bu layihəni bir istiqamətə qoyduq, çünki orijinal layihədə olduğu kimi, ən yaxşı xaş kodunu yaratmaq üçün sual yaranır, belə ki, layihənin ən yaxşısını yaratmaq üçün xahiş edəcəkdir. NET memcpy.

Sonra geri gəldim və hash koduna asanlıqla demək olar ki, bütün tam miqyaslı növləri ( decimal hariç) yükləmək üçün bir çox yüklənmə buraxdı.

Bob Jenkins kreditin ən çox layiq olduğu bu, sürətli, xüsusilə 64 bitli maşınlarda, mən buraxdığım mənbə kodunun daha sürətli olduğu üçün alqoritm ‡ üçün optimallaşdırılmışdır.

Tam kod https://bitbucket.org/JonHanna/spookilysharp/src ünvanından da görünə bilər , lakin yuxarıda göstərilən kodu sadələşdirilmiş bir versiyadır.

Ancaq artıq yazıldığı üçün asanlıqla istifadə edə bilərsiniz:

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

Həm də toxum dəyərlərini qəbul edir, buna görə etibarsız girişlə məşğul olmaq və Hash DoS hücumlarına qarşı qorunmaq istəyirsinizsə, toxumları uptime və ya oxşar olaraq təyin edə bilərsiniz və nəticələrin gözlənilməz hücumçuları edə bilərsiniz:

 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(); } 

* Bu böyük sürpriz rotasiya qaytarma metodunun əl ilə qoyulmasıdır (x << n) | (x >> -n) (x << n) | (x >> -n) (x << n) | (x >> -n) (x << n) | (x >> -n) vəziyyəti yaxşılaşdırdı. Əminəm ki, jitter mənim üçün sərmayə qoyacaq, ancaq profillə başqa bir şey göstərməyib.

C # versiyasına baxmayaraq decimal . NET baxımından doğma deyil. Problem öz GetHashCode() düzgünlüyünü əhəmiyyətli hesab edir və öz Equals() işləmir. Hər ikisi etibarlı, lakin qarışıq deyil. Öz sürümünüzü tətbiq edərkən birinizi və ya birini seçməlisiniz, amma nə istədiyinizi bilmirəm.

‡ müqayisə üçün. SpookyHash, simli istifadə edildikdə 64 bitdən daha sürətli, string.GetHashCode() 32 bitdən daha sürətlidır, bu da string.GetHashCode() 64 bit, SpookyHash 32 bitdən daha sürətlidır, baxmayaraq ki, hələ də sürətli Müdrik bir seçim olmaq üçün kifayətdir.

20
14 янв. Cavab 14 yanvarda Jon Hanna tərəfindən verilir 2014-01-14 17:15 '14 da 17:15 2014-01-14 17:15

Bu yaxşı bir seçimdir:

 /// <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()); } } 

Və burada necə istifadə etmək olar:

 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); } } 
13
07 окт. Cavab Magnus Oct 07 verilir . 2010-10-07 13:51 '10 at 13:51 2010-10-07 13:51

Burada Jon Skeet tərəfindən dərc olunan , lakin boks bölüşdürmələri və ya əməliyyatları olmayan bir alqoritmanın başqa bir tətbiqidir:

 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(); } } } 

İstifadə edin:

 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); } } 

Derleyici, HashValue ümumi bir növü sınırlaması yüzünden bir HashValue təmin edir. HashObject üçün heç bir kompilyator dəstəyi yoxdur, çünki ümumi bir argüman əlavə edərək boks əməliyyatını da əlavə edir.

9
21 янв. 21 yanvarda Scott Wegner tərəfindən verilmiş cavab 2014-01-21 02:41 '14 da 2:41 2014-01-21 02:41

İşdə mənim sadə bir yanaşma. Bunun üçün klassik qurucu şablonunu istifadə edirəm. Bunlar növləri (boks olmadan / açmadan) və həmçinin .NET 2.0 (uzadılması metodları olmadan) ilə uyğun gəlir.

Aşağıdakı kimi istifadə olunur:

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

Və burada acutal qurucu sinif:

 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
22 марта '11 в 15:15 2011-03-22 15:15 cavab 22 mart 2011-ci il tarixində saat 15: 15-də verilir

Https://github.com/dotnet/coreclr/pull/14863 ünvanından başlayaraq, çox sadə olan hash kodları yaratmaq üçün yeni bir yol var! Yalnız yaz

 public override int GetHashCode() => HashCode.Combine(field1, field2, field3); 

Bu, tətbiqetmə təfərrüatları barədə narahatlıq etmədən keyfiyyətli bir hash kodu yaratacaqdır.

6
23 нояб. Noyabrın 23-də James Ko tərəfindən verilmiş cavab 2017-11-23 18:06 '17 at 18:06 2017-11-23 18:06

ReSharper istifadəçiləri ReSharper → Edit → Generate Code → Equality Members istifadə edərək GetHashCode, Equals və digərləri yarada bilər.

 // ReSharper GetHashCode looks like this public override int GetHashCode() { unchecked { int hashCode = Id; hashCode = (hashCode * 397) ^ IntMember; hashCode = (hashCode * 397) ^ OtherIntMember; hashCode = (hashCode * 397) ^ (RefMember != null ? RefMember.GetHashCode() : 0); // ... return hashCode; } } 
4
01 сент. Çərşənbə Burns tərəfindən 01 Sentyabrda verdiyi cavabı 2016-09-01 22:19 '16 saat 10:19 'da 2016-09-01 22:19

Həqiqətən, gecə kodu həll kimi, isterseniz baş nömrələri seçmək daha asandır.

PS: Bu, 9 standartla bir üsula çevrilə biləcəyini bildiyinizə görə, bir az ağız boşaldığınız zamanlardan biridir, ancaq yavaş olacaq, buna görə gözlərinizi bağlayın və bu barədə unutmağı unutmayın.

 /// <summary> /// Try not to look at the source code. It works. Just rely on it. /// </summary> public static class HashHelper { private const int PrimeOne = 17; private const int PrimeTwo = 23; public static int GetHashCode<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10) { unchecked { int hash = PrimeOne; hash = hash * PrimeTwo + arg1.GetHashCode(); hash = hash * PrimeTwo + arg2.GetHashCode(); hash = hash * PrimeTwo + arg3.GetHashCode(); hash = hash * PrimeTwo + arg4.GetHashCode(); hash = hash * PrimeTwo + arg5.GetHashCode(); hash = hash * PrimeTwo + arg6.GetHashCode(); hash = hash * PrimeTwo + arg7.GetHashCode(); hash = hash * PrimeTwo + arg8.GetHashCode(); hash = hash * PrimeTwo + arg9.GetHashCode(); hash = hash * PrimeTwo + arg10.GetHashCode(); return hash; } } public static int GetHashCode<T1, T2, T3, T4, T5, T6, T7, T8, T9>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9) { unchecked { int hash = PrimeOne; hash = hash * PrimeTwo + arg1.GetHashCode(); hash = hash * PrimeTwo + arg2.GetHashCode(); hash = hash * PrimeTwo + arg3.GetHashCode(); hash = hash * PrimeTwo + arg4.GetHashCode(); hash = hash * PrimeTwo + arg5.GetHashCode(); hash = hash * PrimeTwo + arg6.GetHashCode(); hash = hash * PrimeTwo + arg7.GetHashCode(); hash = hash * PrimeTwo + arg8.GetHashCode(); hash = hash * PrimeTwo + arg9.GetHashCode(); return hash; } } public static int GetHashCode<T1, T2, T3, T4, T5, T6, T7, T8>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) { unchecked { int hash = PrimeOne; hash = hash * PrimeTwo + arg1.GetHashCode(); hash = hash * PrimeTwo + arg2.GetHashCode(); hash = hash * PrimeTwo + arg3.GetHashCode(); hash = hash * PrimeTwo + arg4.GetHashCode(); hash = hash * PrimeTwo + arg5.GetHashCode(); hash = hash * PrimeTwo + arg6.GetHashCode(); hash = hash * PrimeTwo + arg7.GetHashCode(); hash = hash * PrimeTwo + arg8.GetHashCode(); return hash; } } public static int GetHashCode<T1, T2, T3, T4, T5, T6, T7>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) { unchecked { int hash = PrimeOne; hash = hash * PrimeTwo + arg1.GetHashCode(); hash = hash * PrimeTwo + arg2.GetHashCode(); hash = hash * PrimeTwo + arg3.GetHashCode(); hash = hash * PrimeTwo + arg4.GetHashCode(); hash = hash * PrimeTwo + arg5.GetHashCode(); hash = hash * PrimeTwo + arg6.GetHashCode(); hash = hash * PrimeTwo + arg7.GetHashCode(); return hash; } } public static int GetHashCode<T1, T2, T3, T4, T5, T6>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) { unchecked { int hash = PrimeOne; hash = hash * PrimeTwo + arg1.GetHashCode(); hash = hash * PrimeTwo + arg2.GetHashCode(); hash = hash * PrimeTwo + arg3.GetHashCode(); hash = hash * PrimeTwo + arg4.GetHashCode(); hash = hash * PrimeTwo + arg5.GetHashCode(); hash = hash * PrimeTwo + arg6.GetHashCode(); return hash; } } public static int GetHashCode<T1, T2, T3, T4, T5>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) { unchecked { int hash = PrimeOne; hash = hash * PrimeTwo + arg1.GetHashCode(); hash = hash * PrimeTwo + arg2.GetHashCode(); hash = hash * PrimeTwo + arg3.GetHashCode(); hash = hash * PrimeTwo + arg4.GetHashCode(); hash = hash * PrimeTwo + arg5.GetHashCode(); return hash; } } public static int GetHashCode<T1, T2, T3, T4>(T1 arg1, T2 arg2, T3 arg3, T4 arg4) { unchecked { int hash = PrimeOne; hash = hash * PrimeTwo + arg1.GetHashCode(); hash = hash * PrimeTwo + arg2.GetHashCode(); hash = hash * PrimeTwo + arg3.GetHashCode(); hash = hash * PrimeTwo + arg4.GetHashCode(); return hash; } } public static int GetHashCode<T1, T2, T3>(T1 arg1, T2 arg2, T3 arg3) { unchecked { int hash = PrimeOne; hash = hash * PrimeTwo + arg1.GetHashCode(); hash = hash * PrimeTwo + arg2.GetHashCode(); hash = hash * PrimeTwo + arg3.GetHashCode(); return hash; } } public static int GetHashCode<T1, T2>(T1 arg1, T2 arg2) { unchecked { int hash = PrimeOne; hash = hash * PrimeTwo + arg1.GetHashCode(); hash = hash * PrimeTwo + arg2.GetHashCode(); return hash; } } } 
3
ответ дан Dbl 21 окт. '14 в 20:49 2014-10-21 20:49

Большая часть моей работы выполняется с подключением к базе данных, что означает, что мои классы имеют уникальный идентификатор из базы данных. Я всегда использую ID из базы данных для генерации хэш-кода.

 // Unique ID from database private int _id; ... { return _id.GetHashCode(); } 
3
ответ дан Mark G 05 нояб. '08 в 8:03 2008-11-05 08:03

Если у нас есть не более 8 свойств (надеюсь), вот еще одна альтернатива.

ValueTuple является структурой и, похоже, имеет GetHashCode реализацию GetHashCode .

Это означает, что мы могли бы просто сделать это:

 // Yay, no allocations and no custom implementations! public override int GetHashCode() => (this.PropA, this.PropB).GetHashCode(); 

Давайте взглянем на текущую реализацию.NET Core для ValueTuple GetHashCode .

Это значение ValueTuple :

  internal static int CombineHashCodes(int h1, int h2) { return HashHelpers.Combine(HashHelpers.Combine(HashHelpers.RandomSeed, h1), h2); } internal static int CombineHashCodes(int h1, int h2, int h3) { return HashHelpers.Combine(CombineHashCodes(h1, h2), h3); } 

И это от HashHelper :

  public static readonly int RandomSeed = Guid.NewGuid().GetHashCode(); public static int Combine(int h1, int h2) { unchecked { // RyuJIT optimizes this to use the ROL instruction // Related GitHub pull request: dotnet/coreclr#1830 uint rol5 = ((uint)h1 << 5) | ((uint)h1 >> 27); return ((int)rol5 + h1) ^ h2; } } 

На английском:

  • Левый поворот (круговой сдвиг) h1 на 5 позиций.
  • Добавьте результат и h1 вместе.
  • XOR - результат с h2.
  • Начните с выполнения указанной операции на {static random seed, h1}.
  • Для каждого последующего элемента выполните операцию над предыдущим результатом и следующим элементом (например, h2).

Было бы неплохо узнать больше о свойствах этого алгоритма хеш-кода ROL-5.

К сожалению, ValueTuple на ValueTuple для нашего собственного GetHashCode может быть не такой быстрой, как хотелось бы и ожидать. Этот комментарий в соответствующем обсуждении показывает, что прямое обращение к HashHelpers.Combine более показательно. С другой стороны, этот является внутренним, поэтому нам нужно будет скопировать код, жертвуя большей частью того, что мы получили здесь. Кроме того, мы будем нести ответственность за то, что помним, чтобы сначала Combine со случайным семенем. Я не знаю, каковы последствия, если мы пропустим этот шаг.

2
ответ дан Timo 15 мая '18 в 15:00 2018-05-15 15:00

Я столкнулся с проблемой с поплавками и десятичными словами, используя реализацию, выбранную в качестве ответа выше.

Этот тест терпит неудачу (floats; hash - это то же самое, хотя я переключил 2 значения как отрицательные):

  var obj1 = new { A = 100m, B = 100m, C = 100m, D = 100m}; var obj2 = new { A = 100m, B = 100m, C = -100m, D = -100m}; var hash1 = ComputeHash(obj1.A, obj1.B, obj1.C, obj1.D); var hash2 = ComputeHash(obj2.A, obj2.B, obj2.C, obj2.D); Assert.IsFalse(hash1 == hash2, string.Format("Hashcode values should be different hash1:{0} hash2:{1}",hash1,hash2)); 

Но этот тест проходит (с ints):

  var obj1 = new { A = 100m, B = 100m, C = 100, D = 100}; var obj2 = new { A = 100m, B = 100m, C = -100, D = -100}; var hash1 = ComputeHash(obj1.A, obj1.B, obj1.C, obj1.D); var hash2 = ComputeHash(obj2.A, obj2.B, obj2.C, obj2.D); Assert.IsFalse(hash1 == hash2, string.Format("Hashcode values should be different hash1:{0} hash2:{1}",hash1,hash2)); 

Я изменил свою реализацию, чтобы не использовать GetHashCode для примитивных типов и, похоже, работает лучше

  private static int InternalComputeHash(params object[] obj) { unchecked { var result = (int)SEED_VALUE_PRIME; for (uint i = 0; i < obj.Length; i++) { var currval = result; var nextval = DetermineNextValue(obj[i]); result = (result * MULTIPLIER_VALUE_PRIME) + nextval; } return result; } } private static int DetermineNextValue(object value) { unchecked { int hashCode; if (value is short || value is int || value is byte || value is sbyte || value is uint || value is ushort || value is ulong || value is long || value is float || value is double || value is decimal) { return Convert.ToInt32(value); } else { return value != null ? value.GetHashCode() : 0; } } } 
1
ответ дан HokieMike 28 сент. '14 в 19:44 2014-09-28 19:44

Microsoft ведет несколько способов хэширования...

 //for classes that contain a single int value return this.value; //for classes that contain multiple int value return x ^ y; //for classes that contain single number bigger than int return ((int)value ^ (int)(value >> 32)); //for classes that contain class instance fields which inherit from object return obj1.GetHashCode(); //for classes that contain multiple class instance fields which inherit from object return obj1.GetHashCode() ^ obj2.GetHashCode() ^ obj3.GetHashCode(); 

Я могу догадаться, что для множественного большого int вы можете использовать это:

 int a=((int)value1 ^ (int)(value1 >> 32)); int b=((int)value2 ^ (int)(value2 >> 32)); int c=((int)value3 ^ (int)(value3 >> 32)); return a ^ b ^ c; 

И то же самое для multi-type: все сначала преобразуются в int с использованием GetHashCode() тогда значения int будут xor'ed, а результат - ваш хэш.

Для тех, кто использует хэш как идентификатор (я имею в виду уникальное значение), хэш, естественно, ограничен числом цифр, я думаю, что это было 5 байтов для алгоритма хеширования, по крайней мере MD5.

Вы можете перевести несколько значений в хешированное значение, а некоторые из них будут одинаковыми, поэтому не используйте его как идентификатор. (возможно, когда-нибудь я буду использовать ваш компонент)

0
ответ дан deadManN 30 нояб. '12 в 22:35 2012-11-30 22:35

Вот класс, который я построил с помощью методов для использования внутри переопределений Equals (объект obj) и GetHashCode(). Я решил использовать generics и алгоритм хэширования, который должен быть способен охватить большинство объектов. Пожалуйста, дайте мне знать, если вы видите что-то здесь, что не работает для некоторых типов объектов, и у вас есть способ его улучшить.

 public class Equality<T> { public int GetHashCode(T classInstance) { List<FieldInfo> fields = GetFields(); unchecked { int hash = 17; foreach (FieldInfo field in fields) { hash = hash * 397 + field.GetValue(classInstance).GetHashCode(); } return hash; } } public bool Equals(T classInstance, object obj) { if (ReferenceEquals(null, obj)) { return false; } if (ReferenceEquals(this, obj)) { return true; } if (classInstance.GetType() != obj.GetType()) { return false; } return Equals(classInstance, (T)obj); } private bool Equals(T classInstance, T otherInstance) { List<FieldInfo> fields = GetFields(); foreach (var field in fields) { if (!field.GetValue(classInstance).Equals(field.GetValue(otherInstance))) { return false; } } return true; } private List<FieldInfo> GetFields() { Type myType = typeof(T); List<FieldInfo> fields = myType.GetTypeInfo().DeclaredFields.ToList(); return fields; } } 

Вот как он используется в классе:

 public override bool Equals(object obj) { return new Equality<ClassName>().Equals(this, obj); } public override int GetHashCode() { unchecked { return new Equality<ClassName>().GetHashCode(this); } } 
0
ответ дан kb4000 14 окт. '17 в 9:09 2017-10-14 09:09

Другие вопросы по меткам или Задайте вопрос