क्यों (ऑब्जेक्ट) 0 == (ऑब्जेक्ट) 0 से अलग ((ऑब्जेक्ट) 0) है। ईक्ल्स ((ऑब्जेक्ट) 0)?


117

निम्नलिखित भाव अलग क्यों हैं?

[1]  (object)0 == (object)0 //false
[2]  ((object)0).Equals((object)0) // true

वास्तव में, मैं पूरी तरह से समझ सकता हूं [1] क्योंकि शायद .NET रनटाइम boxपूर्णांक होगा और इसके बजाय संदर्भों की तुलना करना शुरू कर देगा । लेकिन [2] अलग क्यों है?


36
ठीक है, अब जब आप इस प्रश्न का उत्तर समझते हैं, तो परिणाम की भविष्यवाणी करके अपनी समझ की जाँच करें: short myShort = 0; int myInt = 0; Console.WriteLine("{0}{1}{2}", myShort.Equals(myInt), myInt.Equals(myShort), myInt == myShort); अब इसे वास्तविकता के विरुद्ध जाँचें। क्या आपकी भविष्यवाणी सही थी? यदि नहीं, तो क्या आप विसंगति की व्याख्या कर सकते हैं?
एरिक लिपर्ट

1
@Star, अनुशंसित पढ़ने के लिए देखें msdn.microsoft.com/en-us/library/vstudio/…int16 उर्फ shortबराबरी के तरीके पर उपलब्ध ओवरलोड के लिए , फिर msdn.microsoft.com/en-us/lift/ms173105.aspx देखें। । मैं एरिक लिपर्ट की पहेली को खराब नहीं करना चाहता, लेकिन उन पृष्ठों को पढ़ने के बाद यह पता लगाना बहुत आसान होना चाहिए।
सैम स्कीस

2
मुझे लगा कि यह एक जावा प्रश्न है; कम से कम बराबर में 'ई' देखने से पहले।
सेटेरोपेरे

4
@seteropere जावा वास्तव में अलग है: जावा कैश ऑब्जेक्ट्स में ऑटोबॉक्सिंग, इसलिए ((Integer)0)==((Integer)0)सही का मूल्यांकन करता है।
जूल्स

1
आप भी आजमा सकते हैं IFormattable x = 0; bool test = (object)x == (object)x;। जब कोई बॉक्स पहले से ही हो तो कोई नई बॉक्सिंग नहीं की जाती है।
जेपी स्टिग नीलसन

जवाबों:


151

अलग-अलग व्यवहार करने का कारण यह है कि वे बहुत अलग तरीकों से बंधे हैं।

==मामले स्थिर संदर्भ समानता ऑपरेटर के लिए बाध्य होगा। 2 स्वतंत्र बॉक्सिंग intमूल्य बनाए गए हैं इसलिए वे समान संदर्भ नहीं हैं।

दूसरे मामले में आप इंस्टेंस विधि से बंधते हैं Object.Equals। यह एक आभासी विधि है जो नीचे फ़िल्टर करेगा Int32.Equalsऔर यह एक बॉक्सर पूर्णांक के लिए जांचता है। दोनों पूर्णांक मान 0 हैं इसलिए वे समान हैं


==मामले फोन नहीं करता है Object.ReferenceEquals। यह केवल ceqसंदर्भ तुलना करने के लिए IL निर्देश का उत्पादन करता है ।
सैम हैरवेल

8
@ 280Z28 सिर्फ इसलिए नहीं है क्योंकि संकलक इसे रेखांकित करता है?
अंकन

@ 280Z28 तो? इसी तरह का एक मामला यह है कि उनके बूलियन.ट्रोस्ट्रिंग विधि में सार्वजनिक रूप से उजागर किए गए बूलियन.ट्र्यूस्ट्रिंग और बूलियन.फाल्सी स्ट्रिंजिंग को लौटाने के बजाय स्पष्ट रूप से इसके कार्य के अंदर हार्डकोडिंग स्ट्रिंग्स होते हैं। यह अप्रासंगिक है; मुद्दा ==यह है कि ReferenceEquals(वस्तु पर, वैसे भी) के रूप में एक ही बात करता है । यह सभी सिर्फ एमएस के पक्ष में आंतरिक आंतरिक अनुकूलन है जो अक्सर उपयोग किए जाने वाले कार्यों पर अनावश्यक आंतरिक फ़ंक्शन कॉल से बचने के लिए होता है।
Nyerguds

6
C # भाषा विशिष्टता, पैराग्राफ 7.10.6, कहता है: पूर्वनिर्धारित संदर्भ प्रकार समानता ऑपरेटर हैं: bool operator ==(object x, object y); bool operator !=(object x, object y);ऑपरेटर समानता या गैर-समानता के लिए दो संदर्भों की तुलना करने का परिणाम वापस करते हैं। यह कोई आवश्यकता नहीं है कि System.Object.ReferenceEqualsपरिणाम का निर्धारण करने के लिए विधि का उपयोग किया जाता है। @Markmnl को: नहीं, C # कंपाइलर इनलाइन नहीं करता है, यह कुछ ऐसा है जो कभी-कभी घबराहट करता है (लेकिन इस मामले में नहीं)। तो 280Z28 सही है ReferenceEqualsविधि वास्तव में उपयोग नहीं की जाती है।
जेपी स्टिग नीलसन

@JaredPar: यह दिलचस्प है कि कल्पना कहती है कि, क्योंकि भाषा वास्तव में व्यवहार नहीं करती है। ऊपर दिए गए ऑपरेटरों के रूप में परिभाषित किया गया है, और चर Cat Whiskers; Dog Fido; IDog Fred;(गैर-संबंधित इंटरफेस ICatऔर IDogगैर-संबंधित वर्गों के लिए ) Cat:ICatऔर Dog:IDog, तुलना Whiskers==Fidoऔर Whiskers==34कानूनी होगी (पहले केवल सच हो सकता है अगर व्हिस्की और फिडो दोनों शून्य थे; दूसरा कभी भी सच नहीं हो सकता है; )। वास्तव में, एक C # कंपाइलर दोनों को अस्वीकार कर देगा। सील Whiskers==Fred;होने पर निषिद्ध होगा Cat, लेकिन अनुमति नहीं है अगर यह नहीं है।
सुपरकैट

26

जब आप int मान 0(या किसी अन्य मूल्य प्रकार) को कास्ट करते हैं object, तो मूल्य बॉक्सिंग होता है । प्रत्येक कास्ट objectएक अलग बॉक्स (यानी एक अलग वस्तु उदाहरण) का उत्पादन करने के लिए। ==के लिए ऑपरेटर objectप्रकार प्रदर्शन करती एक संदर्भ तुलना, तो यह गलत रिटर्न के बाद से बाएं ओर और दाएँ हाथ की ओर एक ही उदाहरण नहीं हैं।

दूसरी ओर, जब आप उपयोग करते हैं Equals, जो कि एक आभासी तरीका है, तो यह वास्तविक बॉक्सिंग प्रकार के कार्यान्वयन का उपयोग करता है, अर्थात Int32.Equals, जो दोनों वस्तुओं के समान मूल्य होने के बाद से सही है।


18

==ऑपरेटर, स्थिर किया जा रहा है, आभासी नहीं है। यह सटीक कोड चलाएगा कि objectवर्ग परिभाषित करता है (`ऑब्जेक्ट संकलित समय प्रकार के ऑपरेंड), जो किसी भी ऑब्जेक्ट के रनटाइम प्रकार की परवाह किए बिना, एक संदर्भ तुलना करेगा।

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


==टोकन वास्तव में दो ऑपरेटरों, जिनमें से एक overloadable है और जिनमें से एक नहीं है प्रतिनिधित्व करता है। दूसरे ऑपरेटर का व्यवहार एक अधिभार (वस्तु, वस्तु) से बहुत अलग है।
सुपरकैट

13

Equals()विधि आभासी है।
इसलिए, यह हमेशा ठोस कार्यान्वयन को कॉल करता है, तब भी जब कॉलसेट को डाली जाती है object। मूल्य से तुलना करने के लिए intओवरराइड Equals()करता है, इसलिए आपको मूल्य तुलना मिलती है।


10

== उपयोग: Object.ReferenceEquals

Object.Equals मूल्य की तुलना करता है।

object.ReferenceEqualsविधि संदर्भ तुलना करती है। जब आप किसी ऑब्जेक्ट को आवंटित करते हैं, तो आपको एक संदर्भ प्राप्त होता है जिसमें मेमोरी हीप पर ऑब्जेक्ट के डेटा के अलावा इसकी मेमोरी लोकेशन को दर्शाता है।

object.Equalsविधि वस्तुओं की सामग्री है। यह पहले जाँच करता है कि क्या संदर्भ समान हैं, जैसा कि ऑब्जेक्ट है। संदर्भ। लेकिन फिर यह समानता को और अधिक परीक्षण करने के लिए व्युत्पन्न समान विधियों में कॉल करता है। यह देखो:

   System.Object a = new System.Object();
System.Object b = a;
System.Object.ReferenceEquals(a, b);  //returns true

यद्यपि Object.ReferenceEqualsएक विधि की तरह व्यवहार करता है जो ==अपने ऑपरेंड पर C # ऑपरेटर का उपयोग करता है, C # संदर्भ-समता ऑपरेटर ऑपरेटर (जिसका उपयोग ==ऑपरेंड प्रकारों के लिए उपयोग किया जाता है जिसके लिए कोई अधिभार परिभाषित नहीं है) कॉल करने के बजाय एक विशेष निर्देश का उपयोग करता है ReferenceEquals। इसके अलावा, ऐसे Object.ReferenceEqualsऑपरेंड को स्वीकार करेंगे जो केवल तभी मैच कर सकते हैं जब दोनों अशक्त हों, और ऐसे ऑपरेंड को स्वीकार करेंगे जिन्हें टाइप-सीयर करने की आवश्यकता है Objectऔर इस प्रकार संभवतः कुछ भी मेल नहीं खा सकता है, जबकि संदर्भ-समानता संस्करण ==इस तरह के उपयोग को संकलित करने से इनकार करेगा। ।
सुपरकैट

9

C # ऑपरेटर ==दो अलग-अलग ऑपरेटरों का प्रतिनिधित्व करने के लिए टोकन का उपयोग करता है : एक सांख्यिकीय-अधिभार-तुलना ऑपरेटर और एक गैर-अधिभार-संदर्भ संदर्भ-तुलना ऑपरेटर। जब यह ==टोकन का सामना करता है , तो पहले यह देखता है कि क्या कोई समानता-परीक्षण अधिभार मौजूद है जो ऑपरेंड प्रकारों पर लागू होता है। यदि ऐसा है, तो यह उस अधिभार का आह्वान करेगा। अन्यथा, यह जांच करेगा कि संदर्भ-तुलना ऑपरेटर पर प्रकार लागू हैं या नहीं। यदि हां, तो यह उस ऑपरेटर का उपयोग करेगा। यदि न तो ऑपरेटर ऑपरेटर प्रकारों पर लागू होता है, तो संकलन विफल हो जाएगा।

कोड (Object)0महज एक के Int32लिए नहीं होता है Object: Int32जैसे, सभी मूल्य प्रकार, वास्तव में दो प्रकारों का प्रतिनिधित्व करते हैं, जिनमें से एक मूल्यों और भंडारण स्थानों (जैसे शाब्दिक शून्य) का वर्णन करता है, लेकिन कुछ भी नहीं मिलता है, और जिनमें से एक का वर्णन है ढेर वस्तुओं और से प्राप्त होता है Object; चूँकि केवल बाद वाले प्रकार का ही उपयोग किया जा सकता है Object, इसलिए संकलक को उस बाद वाले प्रकार का एक नया ढेर ऑब्जेक्ट बनाना होगा। प्रत्येक आह्वान (Object)0एक नया ढेर वस्तु बनाता है, इसलिए दो ऑपरेंड ==अलग-अलग ऑब्जेक्ट होते हैं, जिनमें से प्रत्येक स्वतंत्र रूप से, Int32मान 0 को एन्क्रिप्ट करता है ।

क्लास के Objectपास समान संचालक के लिए कोई प्रयोग करने योग्य अधिभार नहीं है। नतीजतन, संकलक ओवरलोडेड समानता-परीक्षण ऑपरेटर का उपयोग करने में सक्षम नहीं होगा, और संदर्भ-समानता परीक्षण का उपयोग करने के लिए वापस गिर जाएगा। क्योंकि दोनों संचालकों को ==अलग-अलग वस्तुओं को संदर्भित करने के लिए, यह रिपोर्ट करेगा false। दूसरी तुलना सफल होती है क्योंकि यह एक ढेर-वस्तु का उदाहरण पूछती है Int32कि क्या यह दूसरे के बराबर है। क्योंकि वह उदाहरण जानता है कि किसी अन्य विशिष्ट उदाहरण के बराबर होने का क्या अर्थ है, यह उत्तर दे सकता है true


इसके अतिरिक्त, हर बार जब आप 0अपने कोड में एक शाब्दिक लिखते हैं तो मुझे लगता है कि यह उसी के लिए ढेर में एक आंतरिक वस्तु बनाता है। यह एक वैश्विक स्थिर शून्य मान का एक अनूठा संदर्भ नहीं है (जैसे कि उन्होंने स्ट्रिंग कैसे बनाया। नई स्ट्रिंग को शुरू करने के लिए नए खाली स्ट्रिंग ऑब्जेक्ट बनाने से बचने के लिए खाली है) इसलिए मुझे पूरा यकीन है कि यहां तक ​​कि एक 0.ReferenceEquals(0)झूठी भी कर देगा, क्योंकि दोनों 0 हैं। नव निर्मित Int32वस्तुएं।
Nyerguds

1
@ नीरग्स, मुझे पूरा यकीन है कि आपके द्वारा कही गई हर बात गलत है, किट्स, हीप, हिस्ट्री, ग्लोबल स्टैटिक्स इत्यादि के बारे 0.ReferenceEquals(0)में फेल हो जाएंगे क्योंकि आप कंपाइल टाइम कंटीन्यू पर किसी तरीके को कॉल करने की कोशिश कर रहे हैं। इसे बंद करने के लिए कोई वस्तु नहीं है। एक अनबॉक्स्ड इंट एक संरचना है, जो स्टैक पर संग्रहीत है। int i = 0; i.ReferenceEquals(...)काम भी नहीं करेगा। क्योंकि System.Int32से विरासत में नहीं मिलता है Object
एंड्रयू बैकर

@AndrewBacker, System.Int32एक struct, एक structहै System.ValueType, जो खुद को विरासत में मिला हैSystem.Object । इसके लिए एक ToString()विधि और एक Equalsविधि हैSystem.Int32
सेबस्टियन

1
फिर भी, Nyerguds यह बताना गलत है कि ढेर पर एक Int32 बनाया जाएगा, जो कि ऐसा नहीं है।
सेबेस्टियन

@ सबस्टियनगोडलेट, मैं उस तरह के आंतरिक को अनदेखा कर रहा हूं। System.Int32 खुद उन तरीकों को लागू करता है। GetType () पर बाहरी है Object, और thats जहाँ मैं इसके बारे में बहुत पहले से चिंता करना बंद कर दिया था। आगे जाना कभी जरूरी नहीं था। AFAIK सीएलआर दो प्रकारों को अलग-अलग और विशेष रूप से संभालता है। यह सिर्फ विरासत नहीं है। हालांकि यह isदो प्रकार के डेटा में से एक है। मैं नहीं चाहता था कि कोई भी उस टिप्पणी को पढ़े और ट्रैक से दूर हो जाए, जिसमें उस तार के बारे में विचित्रता भी शामिल है जो स्ट्रिंग इंटर्निंग को अनदेखा करता है।
एंड्रयू बैकर

3

दोनों चेक अलग-अलग हैं। पहला एक पहचान के लिए जाँच करता है , दूसरा समानता के लिए । सामान्य तौर पर दो शब्द समान होते हैं, यदि वे एक ही वस्तु को संदर्भित कर रहे हों। इसका मतलब है कि वे समान हैं। दो शब्द समान हैं, यदि उनका मान समान है।

प्रोग्रामिंग पहचान के संदर्भ में आमतौर पर संदर्भ समानता द्वारा गड़बड़ की जाती है। यदि दोनों पदों के लिए सूचक समान हैं (!), जिस वस्तु को वे इंगित कर रहे हैं वह बिल्कुल एक ही है। हालाँकि, यदि पॉइंटर्स अलग हैं, तो वे जिस ऑब्जेक्ट को इंगित कर रहे हैं, उसका मूल्य अभी भी बराबर हो सकता है। C # पहचान में स्थिर Object.ReferenceEqualsसदस्य का उपयोग करके जाँच की जा सकती है , जबकि गैर-स्थिर Object.Equalsसदस्य का उपयोग करके समानता की जाँच की जाती है । चूंकि आप दो पूर्णांक को ऑब्जेक्ट्स (जिसे "बॉक्सिंग", btw) कहा जाता है, के ऑपरेटर को पहले चेक ==को objectनिष्पादित करते हैं, जो कि डिफ़ॉल्ट मैपिंग द्वारा होता है Object.ReferenceEqualsऔर पहचान के लिए जांच करता है। यदि आप Equalsअन्वेषण गैर-स्थिर-कॉल , डायनामिक डिस्पैच कॉल को कॉल करते हैं Int32.Equals, जो समानता के लिए जाँच करता है।

दोनों अवधारणाएं समान हैं, लेकिन समान नहीं हैं। वे पहले के लिए भ्रामक लग सकते हैं, लेकिन छोटा अंतर बहुत महत्वपूर्ण है! "ऐलिस" और "बॉब" नामक दो व्यक्तियों की कल्पना करें। वे दोनों एक पीले घर में रह रहे हैं। इस धारणा के आधार पर, कि एलिस और बॉब एक ​​जिले में रह रहे हैं, जहां घर केवल उनके रंग में भिन्न होते हैं, वे दोनों अलग-अलग पीले घरों में रह सकते थे। यदि आप दोनों घरों की तुलना करते हैं, तो आप पहचान लेंगे, कि वे बिल्कुल एक जैसे हैं, क्योंकि वे दोनों पीले हैं! हालांकि, वे एक ही घर को साझा नहीं कर रहे हैं और इस तरह उनके घर समान हैं , लेकिन समान नहीं हैं । पहचान का मतलब यह होगा कि वे एक ही घर में रह रहे हैं ।

ध्यान दें : कुछ भाषाएं ===पहचान की जांच करने के लिए ऑपरेटर को परिभाषित कर रही हैं ।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.