2009-04-18 13 views
12

अधिभावी GetHashCode() के विषय में StackOverflow पर सभी सवाल और जवाब को पढ़ने के बाद मैं GetHashCode() का आसान और सुविधाजनक अधिभावी के लिए निम्नलिखित विस्तार विधि ने लिखा है:GetHashCode एक्सटेंशन विधि

public static class ObjectExtensions 
{ 
    private const int _seedPrimeNumber = 691; 
    private const int _fieldPrimeNumber = 397; 
    public static int GetHashCodeFromFields(this object obj, params object[] fields) { 
     unchecked { //unchecked to prevent throwing overflow exception 
      int hashCode = _seedPrimeNumber; 
      for (int i = 0; i < fields.Length; i++) 
       if (fields[i] != null) 
        hashCode *= _fieldPrimeNumber + fields[i].GetHashCode(); 
      return hashCode; 
     } 
    } 
} 

(मैं मूल रूप से केवल कोड है कि किसी को वहां तैनात पुनर्संशोधित क्योंकि मैं वास्तव में पसंद है यह आम तौर पर इस्तेमाल किया जा सकता है कि)

जो मैं इस तरह का उपयोग करें:

public override int GetHashCode() { 
     return this.GetHashCodeFromFields(field1, field2, field3); 
    } 

क्या आपको इस कोड के साथ कोई समस्या है?

+1

कोड ठीक दिखता है। एक सुधार के रूप में आप जांच सकते हैं कि फ़ील्ड [i] शून्य नहीं है या नहीं। –

+0

बस एक जोड़ा: ऑब्जेक्ट प्रकार में एक्सटेंशन विधियों को जोड़ने के लिए यह बेहद अनुशंसित है। –

+0

मुझे पता है, लेकिन क्यों? हमने वास्तव में किसी भी तरह से ऑब्जेक्ट की कार्यक्षमता को नहीं बदला है। आपकी इंटेलि-भावना केवल अव्यवस्थित हो जाएगी। और इसे एक विस्तार विधि बनाने की भी आवश्यकता नहीं है, यह सिर्फ इसे अधिक दृढ़ बनाता है। आप अभी भी ऑब्जेक्ट में गुजरने वाली स्थिर विधि को कॉल कर सकते हैं। –

उत्तर

2

कि यह करने के लिए एक ठोस तरीका तरह दिखता है।

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

कुछ इस तरह:

public static int GetHashCodeFromFields<T1,T2,T3,T4>(this object obj, T1 obj1, T2 obj2, T3 obj3, T4 obj4) { 
    int hashCode = _seedPrimeNumber; 
    if(obj1 != null) 
     hashCode *= _fieldPrimeNumber + obj1.GetHashCode(); 
    if(obj2 != null) 
     hashCode *= _fieldPrimeNumber + obj2.GetHashCode(); 
    if(obj3 != null) 
     hashCode *= _fieldPrimeNumber + obj3.GetHashCode(); 
    if(obj4 != null) 
     hashCode *= _fieldPrimeNumber + obj4.GetHashCode(); 
    return hashCode; 
} 
+0

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

1

मैं अपने लिए बहुत अच्छा लग रहा हूं, मेरे पास केवल एक मुद्दा है: यह एक शर्म की बात है कि आपको मूल्यों को पारित करने के लिए object[] का उपयोग करना होगा क्योंकि यह आपके द्वारा फ़ंक्शन पर भेजे जाने वाले किसी भी प्रकार के प्रकार को बॉक्स करेगा। मुझे नहीं लगता कि आपके पास बहुत पसंद है हालांकि, जब तक कि आप कुछ जेनेरिक अधिभार बनाने का मार्ग नहीं लेते हैं जैसे कि दूसरों ने सुझाव दिया है।

+0

आईआईआरसी एक वैल्यूएट टाइप पर वर्चुअल फ़ंक्शन कॉल मूल्य को बॉक्स करेगा। – leppie

+1

केवल तभी जब विधि – ShuggyCoUk

0

सामान्य सिद्धांत पर आपको अपने unchecked को जितना संभव हो उतना संकीर्ण रूप से गुंजाइश करना चाहिए, हालांकि इससे कोई फर्क नहीं पड़ता है। इसके अलावा, ठीक लग रहा है।

0
public override int GetHashCode() { 
    return this.GetHashCodeFromFields(field1, field2, field3, this); 
} 

(हाँ, मैं बहुत पंडिताऊ हूँ लेकिन यह है कि मैं देख रहा हूँ केवल एक समस्या है)

+0

विधि के लिए कार्यान्वयन की आपूर्ति नहीं करता है, तो क्या यह अनंत लूप का कारण नहीं बनता है? –

+0

निश्चित रूप से, मुझे यह भी पता नहीं है कि – dfa

+1

को कैसे ठीक किया जाए, यदि आप एक बेवकूफ डेवलपर के खिलाफ अपने सामान्य कार्य को सुरक्षित रखने का प्रयास कर रहे हैं, तो मुझे लगता है कि आप उन फ़ील्ड को देख सकते हैं [i]! = Obj, लेकिन यह एक ऐसा मामला है जहां मैं अगर यह उपयोगकर्ता बेवकूफ़ है तो उसे दुर्घटनाग्रस्त कर दें और जलाएं .... –

0

अधिक इष्टतम:

  1. प्रतिबिंब का उपयोग करता है अपने व्यापार वस्तु क्षेत्रों के माध्यम से देखने के लिए और एक नया आंशिक वर्ग जो GetHashCode ओवरराइड करता है बनाता है एक कोड जनरेटर बनाएँ() (और बराबर())।
  2. जब आपका प्रोग्राम डीबग मोड में शुरू होता है तो कोड जनरेटर चलाएं, और यदि कोड बदल गया है, तो डेवलपर को पुन: संकलित करने के लिए एक संदेश से बाहर निकलें।

इस का लाभ हैं:

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

नुकसान:

  • Overkill यदि आप शब्दकोश लुकअप के बहुत सारे नहीं करते/GetHashCode कॉल()।
0

मैं कहना चाहिए कि आप आवंटन GetHashCode (इसके बारे में यहाँ someusefulblog पदों है) को लागू करने, जबकि ऐसा लगभग कभी नहीं करना चाहिए।

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

+0

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

+0

वे पाते हैं कि पोस्ट के बाद से यह तय हो सकता है ... मैं हैश एक साथ चेनिंग (जेनकींस एक का उपयोग कर एक समय हैश में) मैं इसे में पॉप की कोशिश करता हूँ के लिए कुछ गंधा कार्य बाद में – ShuggyCoUk

+0

(एक 'GetHashCode के साथ गलत कुछ भी नहीं है) 'फ़ंक्शन जो पहली बार स्मृति * आवंटित करता है *, और न ही किसी के साथ कुछ गलत है जिसमें गणना करने में कुछ समय लगता है। ऑब्जेक्ट जिनके हैश फ़ंक्शन जो स्मृति आवंटित करते हैं या गणना करने के लिए समय लेते हैं, हालांकि, उनके हैश मानों को कैश करना चाहिए ताकि बार-बार अनुरोध पूर्ण हो जाएंगे। – supercat

3

मैं कुछ सामान एक छोटे से वापस है कि आप अपनी समस्या को हल कर सकते हैं ... (और वास्तव में, यह शायद बीज है कि आप शामिल करने के लिए सुधार किया जा सकता ...)

वैसे भी, परियोजना है, जबकि लिखा था सार (http://essence.codeplex.com/) कहा जाता है, और यह सिस्टम /Linq.Expression पुस्तकालयों का उपयोग करता है (गुणों के आधार पर) बराबर/GetHashCode/तुलना करने के लिए/ToString के मानक प्रस्तुतियों के साथ-साथ तर्क सूची के आधार पर IEqualityComparer और IComparer कक्षाएं बनाने में सक्षम होने के नाते । (मेरे पास कुछ और विचार भी हैं, लेकिन बहुत आगे बढ़ने से पहले कुछ सामुदायिक फीडबैक प्राप्त करना चाहते हैं।)

(इसका मतलब यह है कि यह हस्तलिखित होने के लगभग तेज़ है - मुख्य जहां यह नहीं है तुलनात्मक(); Linq.Expressions के कारण 3.5 रिलीज में एक चर की अवधारणा नहीं है - इसलिए आपको मिलान प्राप्त करने पर दो बार अंतर्निहित ऑब्जेक्ट पर तुलनात्मक() को कॉल करना होगा। DLR एक्सटेंशन का उपयोग करना Linq.Expressions के लिए यह हल करता है। मुझे लगता है कि मैं emit il का उपयोग कर सकता था, लेकिन मैं उस समय प्रेरित नहीं था।)

यह काफी आसान विचार है, लेकिन मैंने इसे पहले नहीं देखा है।

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

(कोडप्लेक्स साइट में डाउनलोड करने योग्य पैकेज नहीं है; बस स्रोत पर जाएं और इसे पकड़ें - ओह, यह f # में लिखा गया है (हालांकि सभी टेस्ट कोड सी # में हैं) क्योंकि वही चीज़ थी जिसमें मुझे रूचि थी । सीखने)

वैसे भी, यहाँ है परियोजना में परीक्षण से ग # उदाहरण हैं:

// -------------------------------------------------------------------- 
    // USING THE ESSENCE LIBRARY: 
    // -------------------------------------------------------------------- 
    [EssenceClass(UseIn = EssenceFunctions.All)] 
    public class TestEssence : IEquatable<TestEssence>, IComparable<TestEssence> 
    { 
     [Essence(Order=0] public int MyInt   { get; set; } 
     [Essence(Order=1] public string MyString  { get; set; } 
     [Essence(Order=2] public DateTime MyDateTime { get; set; } 

     public override int GetHashCode()        { return Essence<TestEssence>.GetHashCodeStatic(this); } 
    ... 
    } 

    // -------------------------------------------------------------------- 
    // EQUIVALENT HAND WRITTEN CODE: 
    // -------------------------------------------------------------------- 
    public class TestManual 
    { 
     public int MyInt; 
     public string MyString; 
     public DateTime MyDateTime; 

     public override int GetHashCode() 
     { 
      var x = MyInt.GetHashCode(); 
      x *= Essence<TestEssence>.HashCodeMultiplier; 
      x ^= (MyString == null) ? 0 : MyString.GetHashCode(); 
      x *= Essence<TestEssence>.HashCodeMultiplier; 
      x ^= MyDateTime.GetHashCode(); 
      return x; 
     } 
    ... 
    } 

वैसे भी, परियोजना, अगर कोई सोचता है कि सार्थक है, चमकाने जरूरत है, लेकिन विचारों देखते हैं ...

+0

अच्छा, लेकिन हाथ से लिखे गए इम्प्ले से निश्चित रूप से बहुत धीमा। –

+0

नहीं, वास्तव में नहीं। http://essence.codeplex.com/ पर जाएं और इसे एक बर्ल दें! –

0

का उपयोग करने से उत्पन्न होने वाली समस्याओं के अलावा, मुझे लगता है कि कुछ स्थितियों में टाइप जानकारी का उपयोग नहीं करना एक प्रदर्शन समस्या हो सकती है। मान लीजिए कि दो वर्ग A, B में समान प्रकार और फ़ील्ड की संख्या है और उसी इंटरफेस को I लागू करें। अब यदि आप A और B ऑब्जेक्ट्स को Dictionary<I, anything> ऑब्जेक्ट्स के बराबर फ़ील्ड के साथ रखते हैं और विभिन्न प्रकार एक ही बाल्टी में समाप्त हो जाएंगे।मैं शायद hashCode ^= GetType().GetHashCode();

जोनाथन रुपप के स्वीकृत उत्तर पैराम्स सरणी के साथ सौदा करता है लेकिन मूल्य प्रकारों के मुक्केबाजी से निपटने के लिए कुछ कथन डालेंगे। इसलिए, यदि प्रदर्शन बहुत महत्वपूर्ण है तो मैं शायद GetHashCodeFromFields को ऑब्जेक्ट नहीं दे रहा हूं लेकिन int पैरामीटर नहीं, और खेतों को स्वयं ही फ़ील्ड के हैश कोड नहीं भेजता।

hashCode *= _fieldPrimeNumber + fields[i].GetHashCode(); 

: यानी

public override int GetHashCode() 
{ 
    return this.GetHashCodeFromFields(field1.GetHashCode(), field2.GetHashCode()); 
} 
0

एक समस्या यह है कि पैदा कर सकता जब गुणा हिट 0, अंतिम hashCode हमेशा 0 है, के रूप में मैं सिर्फ, संपत्तियों की एक बहुत कुछ के साथ एक वस्तु के साथ अनुभव निम्न कोड में है मेरा सुझाव चाहते हैं:

hashCode = hashCode * _fieldPrimeNumber + fields[i].GetHashCode(); 

या XOR के साथ कुछ इसी तरह this की तरह:

hashCode = hashCode * _fieldPrimeNumber^fields[i].GetHashCode(); 
संबंधित मुद्दे