जब समान विधि ओवरराइड की जाती है, तो GetHashCode को ओवरराइड करना क्यों महत्वपूर्ण है?


1444

निम्न वर्ग को दिया

public class Foo
{
    public int FooId { get; set; }
    public string FooName { get; set; }

    public override bool Equals(object obj)
    {
        Foo fooItem = obj as Foo;

        if (fooItem == null) 
        {
           return false;
        }

        return fooItem.FooId == this.FooId;
    }

    public override int GetHashCode()
    {
        // Which is preferred?

        return base.GetHashCode();

        //return this.FooId.GetHashCode();
    }
}

मैंने Equalsपद्धति को ओवरराइड कर दिया है क्योंकि एस टेबल के Fooलिए एक पंक्ति का प्रतिनिधित्व करता है Foo। ओवरराइडिंग के लिए पसंदीदा तरीका कौन सा है GetHashCode?

ओवरराइड करना क्यों महत्वपूर्ण है GetHashCode?


36
यह विशेष रूप से शब्दकोशों का उपयोग करते समय टकराव के कारण, समान और गेटशोड दोनों को लागू करने के लिए महत्वपूर्ण है। यदि दो ऑब्जेक्ट समान हैशकोड देते हैं, तो उन्हें शब्दकोष में चिनिंग के साथ डाला जाता है। आइटम एक्सेस करते समय समान विधि का उपयोग किया जाता है।
डार्थवेदर

जवाबों:


1319

हां, यह महत्वपूर्ण है कि यदि आपके आइटम को शब्दकोश में एक कुंजी के रूप में उपयोग किया जाएगा, या HashSet<T>, आदि - चूंकि यह उपयोग किया जाता है (कस्टम की अनुपस्थिति में IEqualityComparer<T>) बाल्टी में समूह की वस्तुओं को। यदि दो आइटमों के लिए हैश-कोड मेल नहीं खाता है, तो उन्हें कभी भी समान नहीं माना जा सकता है ( बराबर बस कभी नहीं कहा जाएगा)।

GetHashCode () विधि को प्रतिबिंबित करना चाहिए Equalsतर्क; नियम हैं:

  • यदि दो चीजें समान हैं ( Equals(...) == true) हैं, तो उन्हें उसी मूल्य को वापस करना होगाGetHashCode()
  • यदि GetHashCode()समान है, तो उनके लिए समान होना आवश्यक नहीं है; यह एक टक्कर है, और Equalsयह देखने के लिए बुलाया जाएगा कि यह वास्तविक समानता है या नहीं।

इस मामले में, ऐसा लगता है कि " return FooId;" एक उपयुक्त GetHashCode()कार्यान्वयन है। यदि आप कई गुणों का परीक्षण कर रहे हैं, तो नीचे दिए गए कोड का उपयोग करके उन्हें जोड़ना आम है, विकर्ण टकरावों को कम करने के लिए (यानी ताकि new Foo(3,5)एक अलग हैश-कोड हो new Foo(5,3)):

unchecked // only needed if you're compiling with arithmetic checks enabled
{ // (the default compiler behaviour is *disabled*, so most folks won't need this)
    int hash = 13;
    hash = (hash * 7) + field1.GetHashCode();
    hash = (hash * 7) + field2.GetHashCode();
    ...
    return hash;
}

ओह - सुविधा के लिए, आप भी उपलब्ध कराने पर विचार हो सकता ==है और !=ऑपरेटरों जब अधिभावी Equalsऔर GetHashCode


जब आप इस गलत हो जाते हैं तो क्या होता है, इसका एक प्रदर्शन यहाँ है


49
क्या मैं पूछ सकता हूं कि क्या आप ऐसे कारकों से गुणा कर रहे हैं?
लिएंड्रो लोपेज

22
असल में, मैं शायद उनमें से एक को खो सकता हूं; बिंदु टकरावों की संख्या को कम करने की कोशिश करना है - ताकि एक वस्तु {1,0,0} में एक अलग हैश से {0,1,0} और {0,0,1} (यदि आप देखें कि मेरा क्या मतलब है ),
मार्क Gravell

13
मैंने इसे स्पष्ट करने के लिए संख्याओं को घुमाया (और एक बीज जोड़ा)। कुछ कोड अलग-अलग संख्याओं का उपयोग करते हैं - उदाहरण के लिए C # संकलक (अनाम प्रकारों के लिए) 0x51ed270b के बीज और -1521134295 के एक कारक का उपयोग करता है।
मार्क Gravell

76
@ लिंड्रो लोपेज़: आमतौर पर कारकों को प्रमुख संख्या चुना जाता है क्योंकि यह टकरावों की संख्या को छोटा बनाता है।
आंद्रेई रोनेया

29
"ओह - सुविधा के लिए, आप समान और GethashCode को ओवरराइड करते समय == और = = ऑपरेटर प्रदान करने पर भी विचार कर सकते हैं।": Microsoft उन चीज़ों के लिए ऑपरेटर == को लागू करने को हतोत्साहित करता है जो अपरिवर्तनीय नहीं हैं - msdn.microsoft.com/en-us/library/। ms173147.aspx - "गैर-अपरिवर्तनीय प्रकारों में ऑपरेटर == को ओवरराइड करना एक अच्छा विचार नहीं है।"
एंटीड्यूह

137

यह वास्तव में GetHashCode()सही तरीके से लागू करने के लिए बहुत कठिन है क्योंकि, पहले से ही वर्णित नियमों के अलावा, हैश कोड किसी वस्तु के जीवनकाल के दौरान नहीं बदलना चाहिए। इसलिए हैश कोड की गणना करने के लिए उपयोग किए जाने वाले फ़ील्ड अपरिवर्तनीय होने चाहिए।

मुझे आखिरकार इस समस्या का हल मिल गया जब मैं NHibernate के साथ काम कर रहा था। मेरा दृष्टिकोण ऑब्जेक्ट की आईडी से हैश कोड की गणना करना है। आईडी केवल कंस्ट्रक्टर के लिए सेट की जा सकती है, इसलिए यदि आप आईडी बदलना चाहते हैं, जो बहुत कम है, तो आपको एक नई ऑब्जेक्ट बनानी होगी, जिसमें एक नई आईडी हो और इसलिए एक नया हैश कोड हो। यह दृष्टिकोण GUID के साथ सबसे अच्छा काम करता है क्योंकि आप एक पैरामीटर रहित निर्माता प्रदान कर सकते हैं जो अनियमित रूप से एक आईडी बनाता है।


20
@vanja। मेरा मानना ​​है कि इसके साथ क्या करना है: यदि आप किसी शब्दकोश में ऑब्जेक्ट को जोड़ते हैं और फिर ऑब्जेक्ट की आईडी को बदलते हैं, तो बाद में लाने पर आप इसे पुनः प्राप्त करने के लिए एक अलग हैश का उपयोग करेंगे ताकि आप इसे शब्दकोश से कभी नहीं प्राप्त कर पाएंगे।
ANEves

74
GetHashCode () फ़ंक्शन का Microsoft का दस्तावेज़ीकरण न तो बताता है और न ही इसका मतलब है कि ऑब्जेक्ट हैश जीवनकाल के अनुरूप होना चाहिए। वास्तव में, यह विशेष रूप से एक अनुमेय मामले की व्याख्या करता है जिसमें यह नहीं हो सकता है : "किसी वस्तु के लिए GetHashCode विधि को लगातार उसी हैश कोड को वापस करना चाहिए जब तक कि वस्तु राज्य का कोई संशोधन नहीं होता है जो वस्तु के बराबर पद्धति के वापसी मूल्य को निर्धारित करता है। । "
पीटरअल्बेनवेब

37
"हैश कोड किसी वस्तु के जीवनकाल के दौरान नहीं बदलना चाहिए" - यह सच नहीं है।
सर्वनाश

7
इसे कहने का एक बेहतर तरीका यह है कि "हैश कोड (और न ही बराबर की निकासी) को उस अवधि के दौरान बदलना चाहिए जब ऑब्जेक्ट को एक संग्रह के लिए कुंजी के रूप में उपयोग किया जाता है" इसलिए यदि आप ऑब्जेक्ट को एक शब्दकोश में कुंजी के रूप में जोड़ते हैं, तो आपको यह सुनिश्चित करना चाहिए कि GetHashCode और Equals किसी दिए गए इनपुट के लिए अपने आउटपुट को तब तक नहीं बदलेगा जब तक आप डिक्शनरी से ऑब्जेक्ट को हटा नहीं देते।
स्कॉट चैंबरलेन 5

11
@ScottChamberlain मुझे लगता है कि आप अपनी टिप्पणी में नहीं भूल गए, यह होना चाहिए: "हैश कोड (और न ही बराबर की निकासी) उस अवधि के दौरान नहीं बदलना चाहिए जब ऑब्जेक्ट का उपयोग संग्रह के लिए कुंजी के रूप में किया जाता है"। सही?
स्टेन प्रोकोप

57

बराबरी का ओवरराइड करके आप मूल रूप से कह रहे हैं कि आप वही हैं जो किसी दिए गए प्रकार के दो उदाहरणों की तुलना करना बेहतर जानते हैं, इसलिए आपको सर्वश्रेष्ठ हैश कोड प्रदान करने के लिए सबसे अच्छा उम्मीदवार होने की संभावना है।

यह एक उदाहरण है कि ReSharper आपके लिए एक GetHashCode () फ़ंक्शन कैसे लिखता है:

public override int GetHashCode()
{
    unchecked
    {
        var result = 0;
        result = (result * 397) ^ m_someVar1;
        result = (result * 397) ^ m_someVar2;
        result = (result * 397) ^ m_someVar3;
        result = (result * 397) ^ m_someVar4;
        return result;
    }
}

जैसा कि आप देख सकते हैं कि यह कक्षा के सभी क्षेत्रों के आधार पर एक अच्छे हैश कोड का अनुमान लगाने की कोशिश करता है, लेकिन जब से आप अपनी वस्तु का डोमेन या मूल्य सीमा जानते हैं तब भी आप एक बेहतर प्रदान कर सकते हैं।


7
क्या यह हमेशा शून्य नहीं होगा? शायद परिणाम 1 को इनिशियलाइज़ करना चाहिए! इसके अलावा कुछ और अर्ध-कॉलोन की जरूरत है।
सैम मैकक्रिल

16
आप जानते हैं कि XOR ऑपरेटर (^) क्या करता है?
स्टीफन ड्रू

1
जैसा कि मैंने कहा, यह वह है जो आपके लिए R # लिखता है (कम से कम यह 2008 में वापस आ गया था) जब पूछा गया। जाहिर है, इस स्निपेट को प्रोग्रामर द्वारा किसी तरह से ट्विक करने का इरादा है। अनुपलब्ध अर्ध-कॉलनों के लिए ... हाँ, ऐसा लगता है कि जब मैंने विज़ुअल स्टूडियो में एक क्षेत्र चयन से कोड कॉपी-पेस्ट किया, तो मैंने उन्हें छोड़ दिया। मैंने भी सोचा था कि लोग इसे समझेंगे।
जाल

3
@SamMackrill मैंने लापता अर्ध-कॉलनों में जोड़ा है।
मैथ्यू मर्डोक

5
@SamMackrill नहीं, यह हमेशा 0. नहीं लौटेगा 0 ^ a = a, इसलिए 0 ^ m_someVar1 = m_someVar1। वह आरंभिक मूल्य भी निर्धारित कर सकता resultहै m_someVar1
मिल्ली स्मिथ

41

nullओवरराइडिंग के दौरान ओब्ज पैरामीटर की जांच करना न भूलें Equals()। और प्रकार की भी तुलना करें।

public override bool Equals(object obj)
{
    Foo fooItem = obj as Foo;

    if (fooItem == null)
    {
       return false;
    }

    return fooItem.FooId == this.FooId;
}

इसका कारण यह है: की Equalsतुलना में गलत वापस करना चाहिए nullHttp://msdn.microsoft.com/en-us/library/bsc2ak47.aspx भी देखें


6
प्रकार के लिए यह जांच उस स्थिति में विफल हो जाएगी जहां एक उपवर्ग सुपरक्लास बराबर पद्धति को संदर्भित करता है क्योंकि यह स्वयं की तुलना के आधार के रूप में है (यानी आधार। एक्वाल्स (obj)) - इसके बजाय का उपयोग करना चाहिए
स्वीटफा

@ स्वेफ़ाफ़ा: यह इस बात पर निर्भर करता है कि उपवर्ग का समान तरीका कैसे लागू किया जाता है। इसे आधार भी कह सकते हैं। ईक्वाल्स ((बेसटाइप) ओबज)) जो ठीक काम करेगा।
हुहा

2
: नहीं ऐसा नहीं होगा msdn.microsoft.com/en-us/library/system.object.gettype.aspx । और इसके अलावा, एक विधि का कार्यान्वयन विफल या सफल नहीं होना चाहिए जिस तरह से इसे कहा जाता है। यदि किसी ऑब्जेक्ट का रनटाइम-प्रकार कुछ बेसकेल्स का उपवर्ग है तो बेसकलैस के बराबर () को वास्तविक रूप से वापस लौटना चाहिए अगर objवास्तव में thisकोई फर्क नहीं पड़ता कि कैसे बेसकलैस के बराबर () को बुलाया गया था।
बृहस्पति

2
बढ़ते fooItemशीर्ष करने के लिए और फिर नल के लिए यह जाँच अशक्त के मामले या गलत प्रकार में बेहतर प्रदर्शन करेगा।
इलिडान्स 4 मोनिका को

1
@ 40Alpha खैर, हाँ, तो obj as Fooअमान्य होगा।
IllidanS4 मोनिका को

35

कैसा रहेगा:

public override int GetHashCode()
{
    return string.Format("{0}_{1}_{2}", prop1, prop2, prop3).GetHashCode();
}

प्रदर्शन मान लेना कोई समस्या नहीं है :)


1
erm - लेकिन आप एक int आधारित पद्धति के लिए एक स्ट्रिंग लौटा रहे हैं; _0
jim टोलन

32
नहीं, वह स्ट्रिंग ऑब्जेक्ट से GetHashCode () कॉल करता है, जो एक इंट रिटर्न देता है।
रिचर्ड क्लेटन

3
मैं यह उम्मीद नहीं करता कि मैं जितनी तेजी से हो सकता हूं, न केवल मूल्य प्रकारों के लिए शामिल मुक्केबाजी के लिए, बल्कि प्रदर्शन के लिए भी string.Format। एक और गीकी जो मैंने देखी है new { prop1, prop2, prop3 }.GetHashCode()। खिचड़ी भाषा की टिप्पणी हालांकि इन दोनों में से कौन सी धीमी होगी। साधनों का दुरुपयोग न करें।
नवाफाल

16
यह { prop1="_X", prop2="Y", prop3="Z" }और के लिए सच लौटेगा { prop1="", prop2="X_Y", prop3="Z_" }। आप शायद ऐसा नहीं चाहते।
वॉयट्सजोइबा

2
हां, आप हमेशा अंडरस्कोर प्रतीक को कुछ सामान्य के साथ बदल सकते हैं (जैसे •, you, always, ☺, always, and) और आशा करते हैं कि आपके उपयोगकर्ता इन प्रतीकों का उपयोग नहीं करेंगे ... :)
लुडमिल टिन्कोव

13

हमें सामना करने के लिए दो समस्याएं हैं।

  1. GetHashCode()यदि कोई ऑब्जेक्ट फ़ील्ड में बदला जा सकता है, तो आप एक समझदार प्रदान नहीं कर सकते। इसके अलावा अक्सर एक वस्तु का उपयोग उस संग्रह में किया जाएगा जो निर्भर करता है GetHashCode()। इसलिए कार्यान्वयन की लागत GetHashCode()अक्सर इसके लायक नहीं है, या यह संभव नहीं है।

  2. यदि कोई आपकी वस्तु को एक संग्रह में रखता है जो कॉल करता है GetHashCode()और आपने Equals()बिना GetHashCode()सही व्यवहार किए भी ओवरराइड किया है, तो वह व्यक्ति समस्या को ट्रैक करने में दिन बिता सकता है।

इसलिए डिफ़ॉल्ट रूप से मैं करता हूं।

public class Foo
{
    public int FooId { get; set; }
    public string FooName { get; set; }

    public override bool Equals(object obj)
    {
        Foo fooItem = obj as Foo;

        if (fooItem == null)
        {
           return false;
        }

        return fooItem.FooId == this.FooId;
    }

    public override int GetHashCode()
    {
        // Some comment to explain if there is a real problem with providing GetHashCode() 
        // or if I just don't see a need for it for the given class
        throw new Exception("Sorry I don't know what GetHashCode should do for this class");
    }
}

5
GetHashCode से एक अपवाद को फेंकना ऑब्जेक्ट अनुबंध का उल्लंघन है। एक GetHashCodeफ़ंक्शन को परिभाषित करने में कोई कठिनाई नहीं है जैसे कि कोई भी दो वस्तुएं जो समान हैश कोड को वापस करती हैं; return 24601;और return 8675309;दोनों का मान्य कार्यान्वयन होगा GetHashCodeDictionaryवस्तुओं की संख्या छोटी होने पर ही प्रदर्शन अच्छा होगा, और वस्तुओं की संख्या बड़ी होने पर बहुत खराब हो जाएगा, लेकिन यह किसी भी मामले में सही ढंग से काम करेगा।
सुपरकैट

2
@supercat, GetHashCode को एक समझदारी से लागू करना संभव नहीं है यदि वस्तु में पहचान वाले फ़ील्ड बदल सकते हैं, क्योंकि हैश कोड कभी नहीं बदलना चाहिए। आप जो कहते हैं वह करते हुए किसी को प्रदर्शन की समस्या पर नज़र रखने के लिए कई दिनों तक खर्च करना पड़ सकता है, फिर शब्दकोशों के उपयोग को हटाने के लिए एक बड़े सिस्टम को फिर से डिज़ाइन करने पर कई सप्ताह।
इयान रिंगरोज ने

2
मैं सभी वर्गों के लिए ऐसा कुछ करता था जिसे मैंने परिभाषित किया था कि इक्वल्स () की आवश्यकता थी, और जहां मुझे पूरी तरह से यकीन था कि मैं संग्रह में एक कुंजी के रूप में उस वस्तु का उपयोग कभी नहीं करूंगा। फिर एक दिन एक कार्यक्रम जहां मैंने एक ऑब्जेक्ट का उपयोग किया, जैसे कि एक DevExpress XtraGrid नियंत्रण के लिए इनपुट क्रैश हो गया। यह एक्स्ट्राग्रिड निकला, मेरी पीठ के पीछे, मेरी वस्तुओं के आधार पर एक हैशटेबल या कुछ बना रहा था। मैं DevExpress के साथ एक मामूली बहस में पड़ गया। मैंने कहा कि यह स्मार्ट नहीं था कि वे अस्पष्ट पद्धति के अज्ञात ग्राहक कार्यान्वयन पर अपने घटक की कार्यक्षमता और विश्वसनीयता पर आधारित थे।
रेनीपेट

DevExpress के लोग बल्कि व्यंग्य कर रहे थे, मूल रूप से कह रहा हूं कि मुझे GetHashCode () विधि में एक अपवाद को फेंकने के लिए एक मूर्ख होना चाहिए। मुझे अभी भी लगता है कि उन्हें ऐसा करने का एक वैकल्पिक तरीका ढूंढना चाहिए जो वे कर रहे हैं - मैं मार्क ग्रेवेल को एक अलग धागे पर याद करता हूं जिसमें बताया गया है कि कैसे वह गेटहॉकोड () पर निर्भर हुए बिना मनमानी वस्तुओं का शब्दकोश बनाता है - यह याद नहीं कर सकता कि उसने यह कैसे किया हालांकि।
रेनीपेट

4
@ रेनीपेट, अपवाद को फेंकने के कारण क्रश होना बेहतर है, फिर अवैध कार्यान्वयन के कारण बग को खोजने के लिए बहुत कठिन है।
इयान रिंगरोज ने

12

ऐसा इसलिए है क्योंकि फ्रेमवर्क के लिए आवश्यक है कि दो ऑब्जेक्ट जो समान हों, उनमें एक ही हैशकोड हो। यदि आप दो वस्तुओं की एक विशेष तुलना करने के लिए बराबर विधि को ओवरराइड करते हैं और दो वस्तुओं को विधि द्वारा समान माना जाता है, तो दो वस्तुओं का हैश कोड भी समान होना चाहिए। (शब्दकोश और हैशटेबल्स इस सिद्धांत पर भरोसा करते हैं)।


11

बस उपरोक्त उत्तरों को जोड़ने के लिए:

यदि आप बराबरी से आगे नहीं बढ़ते हैं तो डिफ़ॉल्ट व्यवहार यह है कि वस्तुओं के संदर्भों की तुलना की जाती है। वही हैशकोड पर लागू होता है - डिफ़ॉल्ट रूप से निहितार्थ आम तौर पर संदर्भ के मेमोरी पते पर आधारित होता है। क्योंकि आपने बराबरी कर ली है बराबरी का मतलब यह है कि सही व्यवहार यह है कि आपने जो भी इक्वल्स पर लागू किया है उसकी तुलना करें और संदर्भ नहीं, इसलिए आपको हैशकोड के लिए भी ऐसा ही करना चाहिए।

आपकी कक्षा के ग्राहक हैशकोड से बराबरी की विधि के समान तर्क की उम्मीद करेंगे, उदाहरण के लिए linq के तरीके, जो एक IEqualityComparer का उपयोग करते हैं, पहले हैशकोड की तुलना करते हैं और केवल अगर वे समान हैं तो वे बराबर () विधि की तुलना करेंगे और अधिक महंगा हो सकता है चलाने के लिए, अगर हमने हैशकोड लागू नहीं किया है, तो समान ऑब्जेक्ट में शायद अलग-अलग हैशकोड होंगे (क्योंकि उनके पास अलग-अलग मेमोरी एड्रेस है) और गलत तरीके से निर्धारित किया जाएगा क्योंकि बराबर (बराबर) हिट नहीं होगा।

इसके अलावा, इस समस्या को छोड़कर कि आप अपनी वस्तु को खोजने में सक्षम नहीं हो सकते हैं यदि आपने इसे एक शब्दकोश में उपयोग किया है (क्योंकि यह एक हैशकोड द्वारा डाला गया था और जब आप इसे खोजते हैं तो डिफ़ॉल्ट हैशकोड शायद अलग होगा और फिर से बराबर () मार्क ग्रेवेल की तरह अपने जवाब में बताते हैं कि आपको बुलाया भी नहीं जाएगा, आप डिक्शनरी या हैशसेट अवधारणा का उल्लंघन भी करते हैं, जो समान कुंजी की अनुमति नहीं देनी चाहिए - आप पहले ही घोषित कर चुके हैं कि जब आप ओवररोड करते हैं तो वे वस्तुएं अनिवार्य रूप से समान होती हैं, इसलिए आप समान होते हैं। वे दोनों को एक डेटा संरचना पर अलग-अलग कुंजी के रूप में चाहते हैं, जो एक अद्वितीय कुंजी है मान लें। लेकिन क्योंकि उनके पास एक अलग हैशकोड है "उसी" कुंजी को अलग-अलग के रूप में डाला जाएगा।


8

हैश कोड का उपयोग हैश-आधारित संग्रहों जैसे डिक्शनरी, हैशटेबल, हैशसेट आदि के लिए किया जाता है। इस कोड का उद्देश्य विशिष्ट समूह (बकेट) में डालकर बहुत जल्दी पूर्व-प्रकार की विशिष्ट वस्तु है। यह पूर्व-छँटाई इस वस्तु को खोजने में काफी मदद करती है जब आपको इसे हैश-संग्रह से वापस लाने की आवश्यकता होती है क्योंकि कोड में सभी वस्तुओं के बजाय केवल एक बाल्टी में अपनी वस्तु की खोज करनी होती है। हैश कोड का बेहतर वितरण (बेहतर विशिष्टता) तेजी से पुनर्प्राप्ति। आदर्श स्थिति में जहां प्रत्येक वस्तु का एक अद्वितीय हैश कोड होता है, यह एक O (1) ऑपरेशन है। ज्यादातर मामलों में यह O (1) से संपर्क करता है।


7

यह महत्वपूर्ण नहीं है; यह आपके संग्रह के आकार और आपकी प्रदर्शन आवश्यकताओं पर निर्भर करता है और क्या आपकी कक्षा का उपयोग किसी ऐसी लाइब्रेरी में किया जाएगा जहाँ आप प्रदर्शन आवश्यकताओं को नहीं जानते होंगे। मैं अक्सर जानता हूं कि मेरे संग्रह का आकार बहुत बड़ा नहीं है और मेरा समय एक आदर्श हैक्स कोड बनाकर प्राप्त किए गए प्रदर्शन के कुछ माइक्रोसेकंड से अधिक मूल्यवान है; इसलिए (कंपाइलर द्वारा कष्टप्रद चेतावनी से छुटकारा पाने के लिए) मैं बस उपयोग करता हूं:

   public override int GetHashCode()
   {
      return base.GetHashCode();
   }

(बेशक मैं चेतावनी को बंद करने के लिए एक #pragma का उपयोग कर सकता हूं लेकिन मैं इस तरह से पसंद करता हूं।)

जब आप इस स्थिति में होते हैं कि आपको उन सभी मुद्दों की तुलना में प्रदर्शन की आवश्यकता होती है जो यहां दूसरों द्वारा उल्लिखित हैं, तो निश्चित रूप से लागू होते हैं। सबसे महत्वपूर्ण - अन्यथा हैश सेट या शब्दकोश से आइटम प्राप्त करते समय आपको गलत परिणाम मिलेंगे: हैश कोड किसी वस्तु के जीवन काल के साथ भिन्न नहीं होना चाहिए (अधिक सटीक रूप से, उस समय के दौरान जब भी हैश कोड की आवश्यकता होती है, जैसे कि एक शब्दकोश में एक कुंजी): उदाहरण के लिए, निम्नलिखित गलत है क्योंकि मूल्य सार्वजनिक है और इसलिए उदाहरण के जीवन काल के दौरान इसे बाहरी रूप से कक्षा में बदला जा सकता है, इसलिए आपको इसे हैश कोड के आधार के रूप में उपयोग नहीं करना चाहिए:


   class A
   {
      public int Value;

      public override int GetHashCode()
      {
         return Value.GetHashCode(); //WRONG! Value is not constant during the instance's life time
      }
   }    

दूसरी ओर, यदि मूल्य को बदला नहीं जा सकता है तो इसका उपयोग करना ठीक है:


   class A
   {
      public readonly int Value;

      public override int GetHashCode()
      {
         return Value.GetHashCode(); //OK  Value is read-only and can't be changed during the instance's life time
      }
   }

3
Downvoted। यह सादा गलत है। यहां तक ​​कि Microsoft MSDN ( msdn.microsoft.com/en-us/library/system.getject.gethashcode.aspx ) में कहता है कि GetHashCode का मान तब परिवर्तित होता है जब ऑब्जेक्ट स्थिति एक तरह से बदल जाती है जो कॉल के रिटर्न मूल्य को प्रभावित कर सकती है। बराबर (), और यहां तक ​​कि इसके उदाहरणों में भी यह GetHashCode कार्यान्वयन दिखाता है जो पूरी तरह से सार्वजनिक रूप से परिवर्तनशील मूल्यों पर निर्भर करता है।
सेबस्टियन पीआर गिंगटर

सेबस्टियन, मैं असहमत हूं: यदि आप एक संग्रह में एक वस्तु जोड़ते हैं जो हैश कोड का उपयोग करता है तो इसे हैश कोड पर निर्भर बिन में रखा जाएगा। यदि आप अब हैश कोड बदलते हैं तो आपको संग्रह में फिर से ऑब्जेक्ट नहीं मिलेगा क्योंकि गलत बिन खोजा जाएगा। यह वास्तव में, कुछ ऐसा है जो हमारे कोड में हुआ है और इसीलिए मैंने इसे इंगित करना आवश्यक समझा।
ILoveFortran

2
सेबस्टियन, इसके अलावा, मैं लिंक ( msdn.microsoft.com/en-us/library/system.object.gethashcode.aspx ) में एक बयान नहीं देख सकता कि GetHashCode () को बदलना होगा। इसके विपरीत - इसे तब तक नहीं बदलना चाहिए जब तक कि समान तर्क के लिए समान मान न लौटे: "किसी ऑब्जेक्ट के लिए GetHashCode विधि को लगातार उसी हैश कोड को वापस करना चाहिए जब तक कि ऑब्जेक्ट स्थिति में कोई संशोधन न हो जो रिटर्न मान निर्धारित करता है ऑब्जेक्ट की बराबर पद्धति। "यह कथन विपरीत नहीं है, कि यदि समान रिटर्न के लिए मान बदलता है तो इसे बदलना होगा।
ILoveFortran

2
@ जोआओ, आप निर्माता / कार्यान्वयनकर्ता के साथ अनुबंध के ग्राहक / उपभोक्ता पक्ष को भ्रमित कर रहे हैं। मैं कार्यान्वयनकर्ता की जिम्मेदारी के बारे में बात कर रहा हूं, जो गेटहैशकोड () से आगे निकल जाता है। आप उपभोक्ता के बारे में बात कर रहे हैं, जो मूल्य का उपयोग कर रहा है।
ILoveFortran

1
पूर्ण गलतफहमी ... :) सच्चाई यह है कि हैश कोड तब बदलना चाहिए जब वस्तु की स्थिति तब तक बदल जाती है जब तक कि वस्तु की पहचान के लिए राज्य अप्रासंगिक न हो। इसके अलावा, आपको अपने संग्रह में एक महत्वपूर्ण वस्तु के रूप में कभी भी उपयोग नहीं करना चाहिए। इस उद्देश्य के लिए केवल-पढ़ने के लिए ऑब्जेक्ट का उपयोग करें। GetHashCode, बराबरी ... और कुछ अन्य तरीके जिनके नाम मुझे इस क्षण याद नहीं हैं, उन्हें कभी भी नहीं फेंकना चाहिए।
दार्लोव

0

आपको हमेशा इस बात की गारंटी देनी चाहिए कि यदि दो वस्तुएं समान हैं, जैसा कि बराबर () द्वारा परिभाषित किया गया है, तो उन्हें समान हैश कोड वापस करना चाहिए। जैसा कि कुछ अन्य टिप्पणियों में कहा गया है, सिद्धांत रूप में यह अनिवार्य नहीं है यदि वस्तु का उपयोग हैशसेट या डिक्शनरी जैसे हैश आधारित कंटेनर में कभी नहीं किया जाएगा। मैं आपको सलाह दूंगा कि हमेशा इस नियम का पालन करें। कारण सिर्फ इतना है कि यह किसी के लिए एक प्रकार से दूसरे संग्रह को बदलने के लिए बहुत आसान है क्योंकि वास्तव में प्रदर्शन में सुधार करना या बेहतर तरीके से कोड शब्दार्थ को व्यक्त करना बेहतर है।

उदाहरण के लिए, मान लें कि हम कुछ वस्तुओं को एक सूची में रखते हैं। शायद ही कभी बाद में किसी को पता चलता है कि उदाहरण के लिए बेहतर खोज विशेषताओं के कारण एक हैशसेट एक बेहतर विकल्प है। यह तब है जब हम मुश्किल में पड़ सकते हैं। सूची आंतरिक रूप से उस प्रकार के लिए डिफ़ॉल्ट समानता तुलनित्र का उपयोग करेगी जिसका अर्थ है कि आपके मामले में समान है जबकि हैशट गेटशैकोड () का उपयोग करता है। यदि दोनों अलग-अलग व्यवहार करते हैं, तो आपका कार्यक्रम होगा। और ध्यान रखें कि इस तरह के मुद्दे समस्या निवारण के लिए सबसे आसान नहीं हैं।

मैंने एक ब्लॉग पोस्ट में कुछ अन्य GetHashCode () नुकसान के साथ इस व्यवहार को संक्षेप में प्रस्तुत किया है जहां आप आगे के उदाहरण और स्पष्टीकरण पा सकते हैं।


0

.NET 4.7ओवरराइडिंग की पसंदीदा विधि के रूप में GetHashCode()नीचे दिखाया गया है। यदि पुराने .NET संस्करणों को लक्षित किया जाता है, तो System.ValueTuple nuget पैकेज शामिल करें ।

// C# 7.0+
public override int GetHashCode() => (FooId, FooName).GetHashCode();

प्रदर्शन के संदर्भ में, यह विधि अधिकांश समग्र हैश कोड कार्यान्वयन को बेहतर बनाएगी। ValueTuple एक है structतो वहाँ किसी भी कचरा नहीं होगा, और अंतर्निहित एल्गोरिथ्म के रूप में तेजी से के रूप में यह हो जाता है है।


-1

यह मेरी समझ है कि मूल GetHashCode () ऑब्जेक्ट का मेमोरी एड्रेस लौटाता है, इसलिए यदि आप दो अलग-अलग वस्तुओं की तुलना करना चाहते हैं, तो इसे ओवरराइड करना आवश्यक है।

संपादित करें: यह गलत था, मूल GetHashCode () विधि 2 मानों की समानता का आश्वासन नहीं दे सकती है। हालांकि समान हैं जो ऑब्जेक्ट समान हैश कोड वापस करते हैं।


-6

नीचे प्रतिबिंब का उपयोग मुझे सार्वजनिक गुणों पर विचार करने का एक बेहतर विकल्प लगता है क्योंकि इसके साथ आपको संपत्तियों को हटाने / हटाने के बारे में चिंता करने की ज़रूरत नहीं है (हालांकि इतना सामान्य परिदृश्य नहीं)। यह मुझे बेहतर प्रदर्शन करते हुए भी मिला। (डायग्नॉस्टिक स्टॉप वॉच का तुलनात्मक समय)।

    public int getHashCode()
    {
        PropertyInfo[] theProperties = this.GetType().GetProperties();
        int hash = 31;
        foreach (PropertyInfo info in theProperties)
        {
            if (info != null)
            {
                var value = info.GetValue(this,null);
                if(value != null)
                unchecked
                {
                    hash = 29 * hash ^ value.GetHashCode();
                }
            }
        }
        return hash;  
    }

12
GetHashCode () का कार्यान्वयन बहुत हल्का होने की उम्मीद है। मुझे यकीन नहीं है कि हज़ारों कॉल पर स्टॉपवॉच के साथ प्रतिबिंब का उपयोग किया जा सकता है, लेकिन यह निश्चित रूप से लाखों पर है (एक सूची से एक शब्दकोश को आबाद करने के बारे में सोचें)।
bohdan_trotsenko
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.