2009-03-11 21 views
101

के रूप में किसी ऑब्जेक्ट का उपयोग करना यदि मैं Dictionary के लिए ऑब्जेक्ट्स का उपयोग करना चाहता हूं, तो मुझे उन्हें किसी विशिष्ट तरीके से तुलना करने के लिए ओवरराइड करने के लिए किन तरीकों की आवश्यकता होगी?एक सामान्य शब्दकोश कुंजी

मैं एक एक वर्ग जो गुण है है कहते हैं:

class Foo { 
    public string Name { get; set; } 
    public int FooID { get; set; } 

    // elided 
} 

और मैं एक बनाना चाहते हैं:

Dictionary<Foo, List<Stuff>> 

मैं एक ही समूह के लिए विचार किया जाना ही FooID साथ Foo वस्तुओं चाहते हैं। Foo कक्षा में मुझे किन तरीकों को ओवरराइड करने की आवश्यकता होगी?

संक्षेप में: मैं Foo ऑब्जेक्ट्स द्वारा समूहीकृत सूचियों में Stuff ऑब्जेक्ट्स को वर्गीकृत करना चाहता हूं। Stuff ऑब्जेक्ट्स को उनकी श्रेणी में जोड़ने के लिए FooID होगा।

उत्तर

128

डिफ़ॉल्ट रूप से, दो महत्वपूर्ण तरीकों GetHashCode() और Equals() हैं। यह महत्वपूर्ण है कि यदि दो चीजें बराबर हैं (Equals() सत्य लौटती हैं), कि उनके पास एक ही हैश-कोड है। उदाहरण के लिए, आप "FUID वापस कर सकते हैं;" GetHashCode() के रूप में यदि आप इसे मैच के रूप में चाहते हैं। तुम भी IEquatable<Foo> लागू कर सकते हैं, लेकिन यह वैकल्पिक है:

class Foo : IEquatable<Foo> { 
    public string Name { get; set;} 
    public int FooID {get; set;} 

    public override int GetHashCode() { 
     return FooID; 
    } 
    public override bool Equals(object obj) { 
     return Equals(obj as Foo); 
    } 
    public bool Equals(Foo obj) { 
     return obj != null && obj.FooID == this.FooID; 
    } 
} 

अंत में, एक और विकल्प भी ऐसा ही करने के लिए एक IEqualityComparer<T> प्रदान करना है।

+4

+1 और मेरा मतलब इस धागे को हाइजैक करने का नहीं है, लेकिन मैं इस धारणा के तहत था कि GetHashCode() को FooId.GetHashCode() वापस करना चाहिए। क्या यह सही पैटर्न नहीं है? –

+7

@ केन - ठीक है, इसे केवल एक int वापस करने की आवश्यकता है जो आवश्यक सुविधाएं प्रदान करता है। कौन सा FooID बस के साथ ही FooID.GetHashCode() करेगा। एक कार्यान्वयन विस्तार के रूप में, Int32.GetHashCode() "इसे वापस करें;" है। अन्य प्रकारों (स्ट्रिंग इत्यादि) के लिए, फिर हाँ: .GetHashCode() बहुत उपयोगी होगा। –

+2

धन्यवाद! मैं IEqualityComparer के साथ गया क्योंकि यह केवल डिकारियन के लिए था कि मुझे अतिरंजित तरीकों की आवश्यकता थी। – Dana

8

फू के लिए आप की तुलना करने के दो फू के समान हैं कि क्या object.GetHashCode() और object.Equals()

शब्दकोश प्रत्येक मान के लिए एक हैश बाल्टी की गणना करने के GetHashCode() कहते हैं और बराबर होगा ओवरराइड करने के लिए की आवश्यकता होगी ।

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

public class Foo { 
    public string A; 
    public string B; 

    override bool Equals(object other) { 
      var otherFoo = other as Foo; 
      if (otherFoo == null) 
      return false; 
      return A==otherFoo.A && B ==otherFoo.B; 
    } 

    override int GetHashCode() { 
      return 17 * A.GetHashCode() + B.GetHashCode(); 
    } 
} 
+2

- लेकिन XOR (^) हैश-कोड के लिए एक गरीब Combinator बनाता है, के रूप में यह अक्सर विकर्ण टकराव का एक बहुत की ओर जाता है (यानी {"foo", "bar"} बनाम {"बार", "foo"}। एक बेहतर विकल्प प्रत्येक शब्द को गुणा और जोड़ना है - यानी 17 * ए। गेटहाशकोड() + बी गेटहाशकोड(); –

+2

मार्क, मैं देखें कि आपका क्या मतलब है। लेकिन आप जादू संख्या 17 पर कैसे पहुंचते हैं? क्या हेश के संयोजन के लिए गुणक के रूप में एक प्राइम नंबर का उपयोग करना फायदेमंद है? यदि हां, तो क्यों? – froh42

+0

मई मैं लौटने का सुझाव दे सकता हूं: (ए + बी) .GetHashCode() के बजाय: 17 * ए गेटहाशकोड() + बी गेटहाशकोड() यह होगा: 1) टकराव होने की संभावना कम हो और 2) सुनिश्चित करें कि कोई पूर्णांक ओवरफ़्लो नहीं है। –

29

आप FooID समूह के लिए पहचानकर्ता होना चाहता हूँ के रूप में, आपको लगता है कि का उपयोग करना चाहिए शब्दकोश फू वस्तु के स्थान पर में कुंजी के रूप में:

Dictionary<int, List<Stuff>> 

आप कुंजी के रूप में Foo वस्तु का प्रयोग करेंगे, तो आप केवल FooID संपत्ति पर विचार करने के लिए GetHashCode और Equals विधि को लागू करेगा। Name संपत्ति Dictionary तक केवल वज़न कम हो जाएगी, इसलिए आप Foo को int के लिए एक रैपर के रूप में उपयोग करेंगे।

इसलिए FooID मान सीधे उपयोग करना बेहतर है, और फिर आपको Dictionary के रूप में कुछ भी लागू करने की आवश्यकता नहीं है क्योंकि पहले से ही int कुंजी के रूप में उपयोग करने का समर्थन करता है।

संपादित करें:
तुम वैसे भी कुंजी के रूप में Foo वर्ग का उपयोग करना चाहते हैं, तो IEqualityComparer<Foo> लागू करने के लिए आसान है:

public class FooEqualityComparer : IEqualityComparer<Foo> { 
    public int GetHashCode(Foo foo) { return foo.FooID.GetHashCode(); } 
    public bool Equals(Foo foo1, Foo foo2) { return foo1.FooID == foo2.FooID; } 
} 

उपयोग:

Dictionary<Foo, List<Stuff>> dict = new Dictionary<Foo, List<Stuff>>(new FooEqualityComparer()); 
+1

अधिक सही ढंग से, int पहले से ही एक कुंजी के रूप में उपयोग किए जाने वाले तरीकों/इंटरफेस का समर्थन करता है। शब्दकोश में int या किसी अन्य प्रकार का कोई सीधा ज्ञान नहीं है। –

+0

मैंने इसके बारे में सोचा, लेकिन कई कारणों से यह क्लीनर और शब्दकोश कुंजी के रूप में वस्तुओं का उपयोग करने के लिए अधिक सुविधाजनक था। – Dana

+1

ठीक है, ऐसा लगता है कि आप वस्तु को कुंजी के रूप में उपयोग कर रहे हैं, क्योंकि आप वास्तव में केवल आईडी के रूप में उपयोग कर रहे हैं। – Guffa

1

Hashtable के बारे में क्या वर्ग!

Hashtable oMyDic = new Hashtable(); 
Object oAnyKeyObject = null; 
Object oAnyValueObject = null; 
oMyDic.Add(oAnyKeyObject, oAnyValueObject); 
foreach (DictionaryEntry de in oMyDic) 
{ 
    // Do your job 
} 

ऊपर तरह, आप एक सामान्य शब्दकोश कुंजी :)

0

मैं एक ही समस्या थी के रूप में किसी भी वस्तु (अपनी कक्षा वस्तु) का उपयोग कर सकते हैं। अब मैं इक्विल्स और गेटहाशकोड ओवरराइड करने के कारण किसी भी ऑब्जेक्ट का उपयोग कर एक कुंजी के रूप में उपयोग कर सकता हूं।

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

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); 
     } 
    } 
संबंधित मुद्दे