2010-12-12 17 views
10

structs और classes के लिए समानता जांच लिखने पर आपका दृष्टिकोण क्या है?सी # समानता जांच

1) "पूर्ण" समानता जाँच बॉयलरप्लेट कोड की है कि बहुत आवश्यकता होती है (जैसे override Equals, override GetHashCode, सामान्य Equals, operator==, operator!=)?

2) क्या आप स्पष्ट रूप से निर्दिष्ट करते हैं कि आपके वर्ग IEquatable<T> इंटरफ़ेस मॉडल हैं? जब मैं a == b की तरह कुछ आह्वान और मैं हमेशा दोनों Equals और operator== सदस्यों को लागू करना

3), मैं सही ढंग से समझ, कोई वास्तविक तरीका स्वचालित रूप से Equals ओवरराइड लागू करने के लिए है कि वहाँ है?

उत्तर

20

आप सही हैं, इस बॉयलर-प्लेट कोड का एक बहुत मैं करूंगा है, और आप सब कुछ अलग से लागू करना होगा।

अनुशंसा:

  • आप बिल्कुल मूल्य समानता लागू करने के लिए जा रहे हैं, ओवरराइड GetHashCode और Equals(object) - == के लिए भार के बनाने और कहा कि ऐसा किए बिना IEquatable<T> को लागू करने के लिए बहुत परिणामस्वरूप अनपेक्षित व्यवहार
  • मैं हमेशा IEquatable<T> लागू करेगा अगर आप सकता है अधिभावी Equals(object) और GetHashCode
  • मैं केवल सील न की गयी कक्षाओं के लिए सही ढंग से == ऑपरेटर अधिक शायद ही कभी
  • समानता को लागू करने को ओवरलोड मुश्किल है, और अभी भी आश्चर्य की बात/अवांछनीय परिणाम कर सकते हैं। यदि आपको पदानुक्रम में प्रकारों के लिए समानता की आवश्यकता है, तो IEqualityComparer<T> लागू करें जिसमें आप रुचि रखते हैं।
  • उत्परिवर्तनीय प्रकारों के लिए समानता आमतौर पर एक बुरा विचार है, क्योंकि दो ऑब्जेक्ट बराबर हो सकते हैं और फिर बाद में असमान हो सकते हैं ...यदि किसी ऑब्जेक्ट को एक हैश तालिका में एक कुंजी के रूप में उपयोग करने के बाद (समानता-प्रभावित तरीके से) उत्परिवर्तित किया जाता है, तो आप इसे फिर से नहीं ढूंढ पाएंगे।
  • कुछ बॉयलर प्लेट प्लेटफॉर्म के लिए थोड़ा अलग है ... लेकिन मार्क की तरह, मैं बहुत ही कम ही अपने स्वयं के structs लिखता हूं।

यहां नमूने के कार्यान्वयन है:

using System; 

public sealed class Foo : IEquatable<Foo> 
{ 
    private readonly string name; 
    public string Name { get { return name; } } 

    private readonly int value; 
    public int Value { get { return value; } } 

    public Foo(string name, int value) 
    { 
     this.name = name; 
     this.value = value; 
    } 

    public override bool Equals(object other) 
    { 
     return Equals(other as Foo); 
    } 

    public override int GetHashCode() 
    { 
     int hash = 17; 
     hash = hash * 31 + (name == null ? 0 : name.GetHashCode()); 
     hash = hash * 31 + value; 
     return hash; 
    } 

    public bool Equals(Foo other) 
    { 
     if ((object) other == null) 
     { 
      return false; 
     } 
     return name == other.name && value == other.value; 
    } 

    public static bool operator ==(Foo left, Foo right) 
    { 
     return object.Equals(left, right); 
    } 

    public static bool operator !=(Foo left, Foo right) 
    { 
     return !(left == right); 
    } 
} 

और हाँ, कि बॉयलरप्लेट का एक बहुत की एक बिल्ली है जो की बहुत कम कार्यान्वयन :(

== के कार्यान्वयन थोड़ा है के बीच में परिवर्तन है, इससे कम कुशल हो सकता है, क्योंकि यह Equals(object) पर कॉल करेगा जो गतिशील प्रकार की जांच करने की आवश्यकता है ... लेकिन वैकल्पिक और भी बॉयलर प्लेट है, इस तरह:

public static bool operator ==(Foo left, Foo right) 
{ 
    if ((object) left == (object) right) 
    { 
     return true; 
    } 

    // "right" being null is covered in left.Equals(right) 
    if ((object) left == null) 
    { 
     return false; 
    } 
    return left.Equals(right); 
} 
+1

^^ आपका प्रत्येक पोस्ट देखें एक सी # सीखने का अध्याय है .. :) – Dienekes

+0

2 दूसरे कोड ब्लॉक के लिए मामूली सुझाव: 1) क्या आप '(ऑब्जेक्ट) बाएं == (ऑब्जेक्ट) दाएं' को '==' से सामान्य' बराबर 'तक नहीं ले जाना चाहिए? तो यह गति देता है (निश्चित रूप से यह निर्भर करता है, लेकिन सबसे खराब मामला मानता है) सामान्य 'समान' विधि के लिए संदर्भ समानता की जांच भी करता है? 2) आपको दूसरी नल चेक '(ऑब्जेक्ट) दाएं == नल' की आवश्यकता नहीं है क्योंकि आप अनिवार्य रूप से सामान्य 'समान' में करते हैं। मेरी पोस्ट देखें .. – nawfal

+0

@nawfal: मुझे नहीं लगता कि जेनेरिक 'बराबर' मामले में ऐसा करने में बहुत कुछ है - यह उन मामलों में वैसे भी जल्दी होगा जहां यह सत्य है, और जिन मामलों में यह है * सच नहीं है, यह कोई लाभ के लिए एक अतिरिक्त जांच जोड़ रहा है। शून्य भाग के लिए - उसमें गतिशील प्रकार की जांच की आवश्यकता होगी। हां, आप दोनों के लिए बहस कर सकते हैं - लेकिन मैं दो साल पहले जो लिखा था उससे काफी खुश हूं ... –

1

आपको बस == बी के लिए ऑपरेटर == लागू करने की आवश्यकता है।
जैसा कि मुझे शब्दकोशों में अपना डेटा पसंद है कभी-कभी मैं GetHashCode को ओवरराइड करता हूं।
अगला मैं बराबर लागू करता हूं (एक unmentioned मानक के रूप में ... ऐसा इसलिए है क्योंकि जेनेरिक का उपयोग करते समय समानता के लिए कोई बाधा नहीं है) और लागू करने योग्य Iquatable निर्दिष्ट करें। चूंकि मैं ऐसा करने जा रहा हूं, इसलिए मैं अपने == और! = लागू करने के लिए कार्यान्वयन भी कर सकता हूं। :)

6

मैं कक्षाओं के लिए शायद ही कभी कुछ विशेष करता हूं; अधिकांश नियमित वस्तुओं के संदर्भ में समानता समानता ठीक काम करती है।

मैं शायद ही कभी struct लिखता हूं; लेकिन चूंकि structs मान का प्रतिनिधित्व करते हैं, यह आमतौर पर समानता आदि प्रदान करने के लिए उपयुक्त होता है। इसमें आमतौर पर सबकुछ शामिल होगा; इसके बराबर है, ==,! = और IEquatable<T> (के बाद से इस EqualityComparer<T>.Default का उपयोग कर परिदृश्यों में मुक्केबाजी से बचा जाता है।

बॉयलरप्लेट आमतौर पर भी समस्याग्रस्त नहीं है, लेकिन ReSharper तरह IIRC उपकरण यहाँ कर सकते हैं।

हाँ, यह सलाह दी जाती है सिंक में बराबर है और == रखने के लिए, और इस पर स्पष्ट रूप से किया जाना चाहिए।

संबंधित मुद्दे