c# - बराबर विधि ओवरराइड होने पर GetHashCode को ओवरराइड करना क्यों महत्वपूर्ण है?




override (8)

निम्नलिखित वर्ग को देखते हुए

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

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

        return fooItem.FooId == this.FooId;
    }

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

        return base.GetHashCode();

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

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

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


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


कैसा रहेगा:

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

मान लीजिए प्रदर्शन एक मुद्दा नहीं है :)


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

यह एक उदाहरण है कि 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;
    }
}

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


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

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

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

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


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

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


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

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

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

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

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

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

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

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


Equals() ओवरराइड करते समय कृपया null खिलाफ obj पैरामीटर को देखना न भूलें। और प्रकार की तुलना भी करें।

public override bool Equals(object obj)
{
    if (obj == null || GetType() != obj.GetType())
        return false;

    Foo fooItem = obj as Foo;

    return fooItem.FooId == this.FooId;
}

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





hashcode