2012-03-14 21 views
12

VS2005 प्रलेखन भाग में Guidelines for Overloading Equals() and Operator == (C# Programming Guide) राज्यों गैर अपरिवर्तनीय प्रकार मेंजब .NET क्लास ओवरराइड बराबर होना चाहिए()? यह कब नहीं होना चाहिए?

अधिभावी ऑपरेटर == अनुशंसित नहीं है।

नए .NET फ्रेमवर्क 4 प्रलेखन Guidelines for Implementing Equals and the Equality Operator (==), कि बयान को छोड़ देता है, हालांकि समुदाय सामग्री में एक पोस्ट दावे और संदर्भ पुराने प्रलेखन को दोहराता है।

ऐसा लगता है कि यह इस तरह

public class ImaginaryNumber 
{ 
    public double RealPart { get; set; } 
    public double ImaginaryPart { get; set; } 
} 

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

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

क्या एमएसडीएन गैर-अपरिवर्तनीय प्रकारों के लिए Equals() और operator== ओवरराइड करने के बारे में दिशानिर्देश को हटाने के लिए गलत था?

क्या उत्परिवर्तनीय प्रकारों के लिए बराबर() को ओवरराइड करना उचित है, जहां "वास्तविक दुनिया में" सभी गुणों के समानता का अर्थ है कि ऑब्जेक्ट्स स्वयं बराबर हैं (ImaginaryNumber के साथ)?

यदि यह उचित है, तो संभावित ऑब्जेक्टिवता के साथ सबसे अच्छा कैसे निपटता है जबकि ऑब्जेक्ट इंस्टेंस हैशसेट में भाग ले रहा है या कुछ और जो GetHashCode() पर निर्भर नहीं है?

अद्यतन

बस आमतौर पर, आप मूल्य समानता को लागू जब प्रकार की वस्तुओं हैं जब उनके किसी प्रकार का संग्रह है, या में जोड़े जाने की उम्मीद इस in MSDN

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

+4

है मैं इसे उचित है 'ImaginaryNumber' एक परिवर्तनशील प्रकार होने के लिए नहीं लगता। –

+2

हकीकत में, आपको शायद इमेजिनरी नम्बर को एक अपरिवर्तनीय मान प्रकार (संरचना) के रूप में लागू करना चाहिए। – Joe

+0

वास्तव में? क्यूं कर? अन्य सभी संख्या उत्परिवर्तनीय हैं। –

उत्तर

11

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

सर्वव्यापक परंपरा है कि Equals() का मतलब पहचान समानता का मतलब है कक्षाओं के लिए, और Equals() का अर्थ है structs के लिए मूल्य समानता। इस निर्णय का सबसे बड़ा चालक हैश संग्रह में वस्तुओं का व्यवहार है (Dictionary<T,U>, HashSet<T>, ...) अगर मैं इस सम्मेलन से भटक जाता हूं।

कि निर्णय छोड़ दिया मुझे अभी भी मूल्य समानता की अवधारणा (discussed on MSDN के रूप में)

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

मूल्य समानता (या जैसा कि मैं इसे "समतुल्य" कह रहा हूं) की अवधारणा की इच्छा रखने के लिए एक सामान्य मामला इकाई परीक्षणों में है।

को देखते हुए

public class A 
{ 
    int P1 { get; set; } 
    int P2 { get; set; } 
} 

[TestMethod()] 
public void ATest() 
{ 
    A expected = new A() {42, 99}; 
    A actual = SomeMethodThatReturnsAnA(); 
    Assert.AreEqual(expected, actual); 
} 

परीक्षण क्योंकि Equals() संदर्भ समानता परीक्षण कर रहा है असफल हो जायेगी।

यूनिट परीक्षण निश्चित रूप से प्रत्येक संपत्ति का परीक्षण करने के लिए संशोधित किया जा सकता है, लेकिन यह वर्ग के समकक्षता की अवधारणा को कक्षा के परीक्षण कोड में ले जाता है।

public bool IsEquivalentTo(A other) 
{ 
    if (object.ReferenceEquals(this, other)) return true; 

    if (other == null) return false; 

    bool baseEquivalent = base.IsEquivalentTo((SBase)other); 

    return (baseEquivalent && this.P1 == other.P1 && this.P2 == other.P2); 
} 
:

कि ज्ञान कक्षा में समझाया रखने के लिए, और परीक्षण तुल्यता के लिए एक सुसंगत ढांचा प्रदान करने के, मैं एक अंतरफलक है कि मेरी वस्तुओं को लागू

public interface IEquivalence<T> 
{ 
    bool IsEquivalentTo(T other); 
} 

कार्यान्वयन आम तौर पर इस पद्धति का अनुसरण करता परिभाषित

निश्चित रूप से, यदि मेरे पास पर्याप्त गुणों के साथ पर्याप्त कक्षाएं थीं, तो मैं एक सहायक लिख सकता हूं जो IsEquivalentTo() को लागू करने के लिए प्रतिबिंब के माध्यम से एक अभिव्यक्ति वृक्ष बनाता है।

static public bool IsEquivalentTo<T> 
    (this IEnumerable<T> first, IEnumerable<T> second) 

T तो लागू IEquivalence<T> कि इंटरफेस प्रयोग किया जाता है, अन्यथा Equals() प्रयोग किया जाता है, अनुक्रम के तत्वों की तुलना करने के:

अंत में, मैं एक विस्तार विधि है कि दो IEnumerable<T> की समतुल्यता परीक्षण लागू किया। Equals() पर फ़ॉलबैक की अनुमति देने से यह काम करता है उदा। मेरे व्यापार वस्तुओं के अलावा ObservableCollection<string> के साथ।

अब, मेरी इकाई परीक्षण में जोर

Assert.IsTrue(expected.IsEquivalentTo(actual)); 
+0

हैशकोड पीढ़ी के माध्यम से सोचने और टक्कर और साइड इफेक्ट्स और ब्ला ब्ला ब्लाह के बारे में चिंता करने से यह बहुत अधिक आलसी है। और यह एक अच्छी बात है :) –

+1

मेरी इच्छा है कि .NET ने आभासी समानता परीक्षणों के दो सेट परिभाषित किए हैं (या यह इंगित करने के लिए पैरामीटर शामिल किया गया है कि किस प्रकार की समानता का परीक्षण किया जाना चाहिए)। किसी ऑब्जेक्ट को एक ऑब्जेक्ट इंस्टेंस के लिए एक म्यूटेबल-प्रकार संदर्भ धारण करके डेटा को समाहित करना होता है जो कभी भी किसी भी चीज के संपर्क में नहीं आ सकता है जो इसे बदल सकता है; जिस वस्तु को संदर्भ दिया जाता है उसे समानता परीक्षण की आपूर्ति करनी चाहिए जो उस परिदृश्य में सार्थक होगा। – supercat

+0

@supercat और एरिकजे मैं सहमत हूं। नेट में एक लापता फीचर लगता है। – ToolmakerSteve

11

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

उत्परिवर्तनीय प्रकारों और समानता के आसपास के खतरे आम तौर पर तब दिखाई देते हैं जब उन्हें हैश तालिका में एक कुंजी के रूप में उपयोग किया जाता है या GetHashCode फ़ंक्शन में भाग लेने योग्य सदस्यों को भाग लेने की अनुमति मिलती है।

+0

क्योंकि '==' वास्तव में सी # में दो अलग-अलग ऑपरेटरों का प्रतिनिधित्व करता है, यह बहस योग्य है कि किसी वर्ग प्रकार को वास्तव में इसे अधिभारित करना चाहिए, क्योंकि यह हमेशा स्पष्ट नहीं हो सकता है कि यह संदर्भ समानता परीक्षण या '==' अधिभार के लिए कॉल का प्रतिनिधित्व करता है या नहीं।मैं आगे सुझाव दूंगा कि समानता की एकमात्र धारणा जो सभी वस्तुओं के लिए सार्वभौमिक रूप से लागू होती है, समानता है, और विशिष्ट वर्ग वस्तुएं केवल समकक्ष हो सकती हैं यदि कोई साधन नहीं है जिसके द्वारा वे कभी भिन्न हो सकते हैं। जबकि कुछ "उत्परिवर्तनीय" प्रकार उस मानदंड को पूरा करते हैं, चीजें जिन्हें स्वतंत्र रूप से उत्परिवर्तित किया जा सकता है, नहीं। – supercat

+0

पुन "दो आइटम अब बराबर हो सकते हैं भले ही वे भविष्य में बदल जाएंगे।" समस्या यह है कि यदि आप एक mutable प्रकार के लिए == बदलते हैं, तो कोई भी उस प्रकार के किसी ऑब्जेक्ट को एक कुंजीपटल कुंजी के रूप में सुरक्षित रूप से उपयोग नहीं कर सकता है। यह बहुत बड़ी समस्या है। मैंने मुझे अपने आप को लिखा, यहां तक ​​कि मैंने खुद को लिखा है, इसलिए मुझे बेहतर पता होना चाहिए। यह माइक्रोसॉफ्ट द्वारा एक गंभीर डिजाइन दोष है। उत्परिवर्तनों के लिए एकमात्र सुरक्षित उत्तर, जैसे कि एरिकजे और सुपरकैट अन्य उत्तरों में चर्चा करता है, अकेले बराबर छोड़ना है, और एक अलग नाम के साथ अपने स्वयं के समानता समारोह को परिभाषित करना है। परेशान, क्योंकि अब आप अपनी वस्तुओं की तुलना करते समय एक अलग समारोह का उपयोग करना होगा। – ToolmakerSteve

0

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

संपादित

@Erik जम्मू के लिए धन्यवाद मैं बिंदु को देखते हैं।

HashSet<T> एक प्रदर्शन संग्रह है और यह प्रदर्शन प्राप्त करने के लिए यह संग्रह के जीवन के लिए निरंतर GetHashCode पर निर्भर करता है। यदि आप यह प्रदर्शन चाहते हैं तो आपको इन नियमों का पालन करना होगा। आप नहीं तो आप की तरह List<T>

+0

यदि आप हैशसेट में जोड़े जाने के बाद किसी ऑब्जेक्ट के हैश कोड को बदलते हैं, तो 'myHashSet.Contains (myMutatedObject)' झूठी रिटर्न देता है। हालांकि, हैशसेट में अभी भी (अब उत्परिवर्तित) ऑब्जेक्ट शामिल है। इसलिए, हैश वैल्यू को बदलने के लिए हैशसेट अनुबंध ('ब्रेक शामिल है) को तोड़ने की अनुमति देता है। –

+0

अब मैं बिंदु देखता हूं, धन्यवाद! –

6

चेक बाहर Eric Lippert द्वारा Guidelines and rules for GetHashCode कुछ और करने के लिए स्विच करना होगा कर सकते हैं।

नियम: पूर्णांक GetHashCode द्वारा दिया जबकि वस्तु एक डेटा संरचना है कि शेष हैश कोड पर निर्भर करता है में निहित है कभी नहीं करना चाहिए स्थिर

यह अनुमति है, हालांकि खतरनाक है, एक वस्तु जिसका हैश बनाने के लिए कोड मान ऑब्जेक्ट mutate के क्षेत्रों के रूप में mutate कर सकते हैं।

+1

मैंने वास्तव में पहले इसे पढ़ा था, और उन लोगों के लिए पढ़ने योग्य है जो नहीं हैं। असल में मुझे लगता है कि यह पंक्ति मेरे प्रश्न का सबसे अच्छा जवाब देती है: 'यदि आपके पास ऐसी कोई वस्तु है और आप इसे हैश तालिका में डालते हैं तो कोड जो ऑब्जेक्ट को बदलता है और हैश तालिका को बनाए रखने वाले कोड को कुछ सहमत प्रोटोकॉल होने की आवश्यकता होती है यह सुनिश्चित करता है कि हैश तालिका में ऑब्जेक्ट को उत्परिवर्तित नहीं किया गया है। वह प्रोटोकॉल कैसा दिखता है वह आपके ऊपर है। –

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