चूँकि मुझे ऐसा उत्तर नहीं मिला जो बताता है कि हमें ओवरराइड करना चाहिए GetHashCode
और Equals
कस्टम संरचनाओं के लिए और डिफ़ॉल्ट कार्यान्वयन "हैश तालिका में कुंजी के रूप में उपयोग के लिए उपयुक्त होने की संभावना क्यों नहीं है", मैं इस ब्लॉग का लिंक छोड़ दूंगा पोस्ट , जो बताता है कि क्यों एक समस्या का वास्तविक मामला उदाहरण के साथ हुआ।
मैं पूरी पोस्ट पढ़ने की सलाह देता हूं, लेकिन यहां एक सारांश (जोर और स्पष्टीकरण जोड़ा गया है) है।
कारण संरचना के लिए डिफ़ॉल्ट हैश धीमा है और बहुत अच्छा नहीं है:
सीएलआर को जिस तरह से डिज़ाइन किया गया है, एक सदस्य को हर कॉल System.ValueType
या System.Enum
प्रकार में परिभाषित किया जाता है [हो सकता है] एक बॉक्सिंग आवंटन का कारण बन सकता है [...]
हैश फ़ंक्शन के एक कार्यान्वयनकर्ता को एक दुविधा का सामना करना पड़ता है: हैश फ़ंक्शन का अच्छा वितरण करना या इसे तेज़ करना। कुछ मामलों में, उन दोनों को हासिल करना संभव है, लेकिन यह उदारतापूर्वक करना मुश्किल है ValueType.GetHashCode
।
सभी क्षेत्रों के एक "संयोजन" हैश कोड के कैनोनिकल हैश फ़ंक्शन। लेकिन एक ValueType
विधि में किसी फ़ील्ड का हैश कोड प्राप्त करने का एकमात्र तरीका प्रतिबिंब का उपयोग करना है । इसलिए, सीएलआर लेखकों ने वितरण से अधिक गति का व्यापार करने का फैसला किया और डिफ़ॉल्ट GetHashCode
संस्करण बस पहले गैर-शून्य फ़ील्ड का एक हैश कोड लौटाता है और इसे "munges" टाइप आईडी के साथ [...] यह एक उचित व्यवहार है जब तक कि यह नहीं है। । उदाहरण के लिए, यदि आप पर्याप्त रूप से अशुभ हैं और आपकी संरचना के पहले क्षेत्र में अधिकांश उदाहरणों के लिए समान मूल्य है, तो एक हैश फ़ंक्शन हर समय एक ही परिणाम प्रदान करेगा । और, जैसा कि आप कल्पना कर सकते हैं, यह एक कठोर प्रदर्शन प्रभाव का कारण होगा यदि ये उदाहरण एक हैश सेट या एक हैश तालिका में संग्रहीत किए जाते हैं।
[...] परावर्तन आधारित कार्यान्वयन धीमा है । बहुत धीमी गति से।
[...] दोनों ValueType.Equals
और ValueType.GetHashCode
एक विशेष अनुकूलन है। यदि एक प्रकार में "पॉइंटर्स" नहीं हैं और ठीक से पैक किया गया है [...] तो अधिक इष्टतम संस्करणों का उपयोग किया जाता है: GetHashCode
4 बाइट्स और XORs ब्लॉक के पुनरावृत्तियों Equals
का उपयोग करता है और विधि दो इंस्टेंस का उपयोग करके तुलना करती है memcmp
। [...] लेकिन अनुकूलन बहुत मुश्किल है। पहले, यह जानना कठिन है कि अनुकूलन कब सक्षम किया जाता है [...] दूसरा, एक स्मृति तुलना आवश्यक रूप से आपको सही परिणाम नहीं देगी । यहाँ एक सरल उदाहरण है: [...] -0.0
और +0.0
समान हैं लेकिन अलग-अलग बाइनरी प्रतिनिधित्व हैं।
पोस्ट में वर्णित वास्तविक दुनिया का मुद्दा:
private readonly HashSet<(ErrorLocation, int)> _locationsWithHitCount;
readonly struct ErrorLocation
{
// Empty almost all the time
public string OptionalDescription { get; }
public string Path { get; }
public int Position { get; }
}
हमने एक टपल का उपयोग किया जिसमें डिफ़ॉल्ट समानता कार्यान्वयन के साथ एक कस्टम संरचना शामिल थी। और दुर्भाग्य से, संरचना में एक वैकल्पिक पहला क्षेत्र था जो लगभग हमेशा [खाली स्ट्रिंग] के बराबर होता है । प्रदर्शन तब तक ठीक था जब तक कि सेट में तत्वों की संख्या एक वास्तविक प्रदर्शन मुद्दा नहीं बन गई, दसियों हज़ारों मदों के साथ एक संग्रह को आरंभ करने में मिनट लग गए।
तो, इस सवाल का जवाब करने के लिए "क्या मामलों में मैं पैक चाहिए मेरे अपने और क्या मामलों मैं सुरक्षित रूप से डिफ़ॉल्ट कार्यान्वयन पर भरोसा कर सकते में", के मामले में कम से कम structs , आप ओवरराइड करना चाहिए Equals
और GetHashCode
अपने कस्टम struct एक के रूप में इस्तेमाल किया जा सकता है जब भी एक हैश तालिका में कुंजी या Dictionary
।
मैं IEquatable<T>
मुक्केबाजी से बचने के लिए इस मामले में भी लागू करने की सिफारिश करूंगा ।
जैसा कि अन्य उत्तर में कहा गया है, यदि आप एक वर्ग लिख रहे हैं, तो संदर्भ समानता का उपयोग करने वाला डिफ़ॉल्ट हैश आमतौर पर ठीक है, इसलिए मैं इस मामले में परेशान नहीं करूंगा, जब तक कि आपको ओवरराइड करने की आवश्यकता नहीं है Equals
(तब आपको GetHashCode
तदनुसार ओवरराइड करना होगा )।