C # में प्राथमिकताओं के लिए == और बराबर () के बीच क्या अंतर है?


180

इस कोड पर विचार करें:

int age = 25;
short newAge = 25;
Console.WriteLine(age == newAge);  //true
Console.WriteLine(newAge.Equals(age)); //false
Console.ReadLine();

दोनों intऔर shortआदिम प्रकार के साथ तुलना कर रहे हैं, लेकिन ==रिटर्न सही और के साथ एक तुलना Equalsझूठे रिटर्न।

क्यों?


9
@OrangeDog कृपया प्रश्न के बारे में सोचो और फिर के करीब वोट

4
यह स्पष्ट उलट प्रयास को याद कर रहा है:Console.WriteLine(age.Equals(newAge));
ANeves

3
डुप्लिकेट इस व्यवहार की व्याख्या नहीं करता है; यह Equals()सामान्य तौर पर क्या है, इसके बारे में है।
12

37
मैंने कुछ दिन पहले कवरेज ब्लॉग पर इस सटीक प्रश्न का उत्तर दिया था। blog.coverity.com/2014/01/13/inconsistent-equality
Eric Lippert

5
@CodesInChaos: विनिर्देश वास्तव में कभी भी इसे परिभाषित किए बिना दो बार "आदिम प्रकार" शब्द का उपयोग करता है; निहितार्थ यह है कि आदिम प्रकार में निर्मित मूल्य प्रकार होते हैं, लेकिन यह कभी स्पष्ट नहीं होता है। मैंने मैड्स से सिफारिश की है कि यह शब्द विशेष रूप से विनिर्देशन से त्रस्त है क्योंकि यह हटाने से अधिक भ्रम पैदा करता है।
एरिक लिपर्ट

जवाबों:


262

संक्षिप्त जवाब:

समानता जटिल है।

विस्तृत जवाब:

प्राइमिटिव प्रकार आधार को ओवरराइड करते हैं object.Equals(object)और अगर बॉक्सेड objectएक ही प्रकार और मूल्य का है तो सही लौटें । (ध्यान दें कि यह अशक्त प्रकारों के लिए भी काम करेगा; गैर-अशक्त प्रकार हमेशा अंतर्निहित प्रकार के उदाहरण के लिए बॉक्स में होता है।)

चूंकि newAgeshort, इसकी Equals(object)विधि केवल तभी सही होती है जब आप उसी मूल्य के साथ एक बॉक्सिंग शॉर्ट पास करते हैं । आप एक बॉक्सिंग पास कर रहे हैं int, इसलिए यह गलत है।

इसके विपरीत, ==ऑपरेटर को दो intएस (या shortएस या longएस) लेने के रूप में परिभाषित किया गया है ।
जब आप इसे एक साथ फोन intऔर एक short, संकलक परोक्ष में परिवर्तित कर देंगे shortकरने के लिए intऔर जिसके परिणामस्वरूप तुलना intमूल्य द्वारा रों।

इसे काम करने के अन्य तरीके

आदिम प्रकार की भी अपनी Equals()विधि है जो उसी प्रकार को स्वीकार करती है।
यदि आप लिखते हैं age.Equals(newAge), तो संकलक int.Equals(int)सर्वश्रेष्ठ अधिभार के रूप में चयन करेगा और अंतर्निहित रूप से परिवर्तित shortहो जाएगा int। यह तब वापस आ जाएगा true, क्योंकि यह विधि intसीधे एस की तुलना करती है।

shortइसकी एक short.Equals(short)विधि भी है , लेकिन intइसे संक्षेप में परिवर्तित नहीं किया जा सकता है short, इसलिए आप इसे कॉल नहीं कर रहे हैं।

आप इस विधि को कलाकारों के साथ कॉल करने के लिए मजबूर कर सकते हैं:

Console.WriteLine(newAge.Equals((short)age)); // true

यह short.Equals(short)बॉक्सिंग के बिना, सीधे कॉल करेगा । यदि age32767 से बड़ा है, तो यह एक अतिप्रवाह अपवाद फेंक देगा।

आप short.Equals(object)अधिभार को भी कॉल कर सकते हैं , लेकिन स्पष्ट रूप से एक बॉक्सिंग ऑब्जेक्ट को पास करें ताकि उसे एक ही प्रकार मिले:

Console.WriteLine(newAge.Equals((object)(short)age)); // true

पिछले विकल्प की तरह, यह एक अतिप्रवाह को फेंक देगा यदि यह एक में फिट नहीं होता है short। पिछले समाधान के विपरीत, यह shortसमय और स्मृति को बर्बाद करते हुए एक वस्तु में बॉक्स कर देगा ।

सोर्स कोड:

यहाँ Equals()वास्तविक स्रोत कोड से दोनों तरीके हैं :

    public override bool Equals(Object obj) {
        if (!(obj is Int16)) {
            return false;
        }
        return m_value == ((Int16)obj).m_value;
    }

    public bool Equals(Int16 obj)
    {
        return m_value == obj;
    }

आगे की पढाई:

एरिक लिपर्ट को देखें ।


3
@SLaks, अगर हम कहते हैं long == int, intपरोक्ष में बदल जाती longहै ना?
सेलमैन जेनकी

1
और हाँ, मैंने वह सब लिखा जो वास्तव में इसे आजमाए बिना था।
SLKs

1
याद रखें कि, प्रश्न के कोड में, यदि कोई परिवर्तन int age = 25;करता है const int age = 25;, तो परिणाम बदल जाएगा। ऐसा इसलिए है क्योंकि से एक अंतर्निहित रूपांतरण है intकरने के लिए shortउस मामले में मौजूद है। अव्यवस्थित स्थिर अभिव्यक्ति रूपांतरण देखें ।
जेपी स्टिग नीलसन

2
@SLaks हाँ, लेकिन आपके उत्तर "पारित मूल्य" के शब्दों को दोनों तरीकों से व्याख्या किया जा सकता है (जैसा कि डेवलपर द्वारा पारित मूल्य, या मूल्य जो वास्तव में सीएलआर द्वारा अनबॉक्सिंग के बाद पारित किया जाता है)। मैं उस आकस्मिक उपयोगकर्ता का अनुमान लगा रहा हूं, जो पहले से ही यहां के उत्तर नहीं जानता है, इसे पूर्व की तरह पढ़ेगा
JaredPar

2
@ राशेल: सिवाय इसके कि यह सच नहीं है; डिफ़ॉल्ट == ऑपरेटर संदर्भ द्वारा संदर्भ प्रकार है। मान प्रकारों के लिए, और उन प्रकारों के लिए जो अतिभारित करते हैं ==, यह नहीं है।
SLACs

55

वहाँ है क्योंकि के लिए कोई अधिभार short.Equalsहै कि एक को स्वीकार करता है int। इसलिए, इसे कहा जाता है:

public override bool Equals(object obj)
{
    return obj is short && this == (short)obj;
}

objएक short.. नहीं है , इसलिए, यह गलत है।


12

जब आप पास intहो shortजाते हैं तो बराबर हो जाते हैंobject :

यहां छवि विवरण दर्ज करें तो यह छद्मकोड चलता है:

return obj is short && this == (short)obj;

12

मान प्रकारों के लिए, .Equalsदो वस्तुओं को एक ही प्रकार के होने की आवश्यकता होती है और उनका मूल्य समान होता है, जबकि ==दोनों मान समान होने पर ही परीक्षण करते हैं।

Object.Equals
http://msdn.microsoft.com/en-us/library/bsc2ak47(v=vs.110).aspx


10

== का उपयोग एक समान स्थिति की जाँच के लिए किया जाता है, इसे एक ऑपरेटर (बूलियन ऑपरेटर) के रूप में माना जा सकता है, बस 2 चीजों की तुलना करने के लिए और यहाँ डेटा प्रकार कोई फर्क नहीं पड़ता क्योंकि वहाँ एक प्रकार की ढलाई होगी Equals इसकी बराबरी की जाँच के लिए भी उपयोग किया जाता है , लेकिन इस मामले में डेटा प्रकार समान होना चाहिए। एन इक्वल्स एक ऐसा तरीका है जो ऑपरेटर नहीं है।

नीचे एक छोटा उदाहरण दिया गया है जिसे आपने प्रदान किया है और यह संक्षेप में अंतर स्पष्ट करेगा,।

int x=1;
short y=1;
x==y;//true
y.Equals(x);//false

उपरोक्त उदाहरण में, X और Y के समान मूल्य हैं अर्थात 1, और जब हम उपयोग करते हैं ==, तो यह सच हो जाएगा, जैसे कि== , संक्षिप्त प्रकार को कंपाइलर द्वारा इंट में बदल दिया जाता है और परिणाम दिया जाता है।

और जब हम उपयोग करते हैं Equals , तो तुलना की जाती है, लेकिन कंपाइलर द्वारा टाइप कास्टिंग नहीं की जाती है, इसलिए गलत लौटा दिया जाता है।

दोस्तों, कृपया मुझे बताएं कि क्या मैं गलत हूं।


6

कई संदर्भों में जहां एक विधि या ऑपरेटर तर्क आवश्यक प्रकार का नहीं है, C # संकलक एक अंतर्निहित प्रकार रूपांतरण करने का प्रयास करेगा। यदि संकलक सभी रूपांतरणों को जोड़कर अपने संचालकों और तरीकों को संतुष्ट कर सकता है, तो यह शिकायत के बिना ऐसा करेगा, भले ही कुछ मामलों में (विशेषकर समानता परीक्षण के साथ!) परिणाम आश्चर्यजनक हो सकते हैं।

इसके अलावा, प्रत्येक मूल्य प्रकार जैसे कि intया shortवास्तव में एक प्रकार का मूल्य और एक प्रकार की वस्तु (*) दोनों का वर्णन करता है। निहित रूपांतरण, मूल्यों को अन्य प्रकार के मूल्यों में परिवर्तित करने के लिए, और किसी भी प्रकार के मूल्य को अपनी इसी प्रकार की वस्तु में परिवर्तित करने के लिए मौजूद हैं, लेकिन विभिन्न प्रकार की वस्तुएं एक दूसरे के लिए अनुमानित रूप से परिवर्तनीय नहीं हैं।

एक का उपयोग करता है ==एक तुलना करने के लिए ऑपरेटर shortऔर एक int, shortपरोक्ष एक में परिवर्तित हो जाएगा int। यदि इसका संख्यात्मक मान इसके बराबर था int, तो intइसे जिस रूप में परिवर्तित किया गया था int, वह उसकी तुलना में समान होगा । यदि कोई Equalsइसे के साथ तुलना करने के लिए शॉर्ट पर विधि का उपयोग करने का प्रयास करता है int, हालांकि, केवल अंतर्निहित रूपांतरण जो Equalsविधि के अधिभार को संतुष्ट करेगा, उसी के अनुरूप ऑब्जेक्ट प्रकार में रूपांतरण होगा int। जब shortपूछा जाता है कि क्या यह पारित कर दिया-वस्तु से मेल खाता है, यह निरीक्षण है कि प्रश्न में वस्तु एक है जाएगा intबल्कि एक से shortऔर इस प्रकार निष्कर्ष है कि यह संभवतः बराबर नहीं हो सकता।

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

int i = 16777217;
float f = 16777216.0f;

Console.WriteLine("{0}", i==f);

तीन तरीके हैं जिनमें से एक की तुलना करना चाहते हो सकता intहैfloat । एक जानना चाहते हो सकता है:

  1. क्या निकटतम संभव floatमान हैint मैच केfloat ?
  2. की पूरी-संख्या का हिस्सा है floatमैच है int?
  3. intऔर करोfloatएक ही संख्यात्मक मान का प्रतिनिधित्व ।

यदि कोई सीधे intऔर floatसीधे तुलना करने की कोशिश करता है , तो संकलित कोड पहले प्रश्न का उत्तर देगा; क्या यह है कि प्रोग्रामर का इरादा है, हालांकि, स्पष्ट से दूर हो जाएगा। तुलना बदलने से (float)i == fयह स्पष्ट हो जाएगा कि पहला अर्थ इरादा था, या (double)i == (double)fतीसरे प्रश्न का उत्तर देने के लिए कोड का कारण होगा (और यह स्पष्ट करें कि वह क्या था)।

(*) भले ही C # कल्पना प्रकार का एक मान मानती है , जैसे कि एक System.Int32प्रकार की वस्तु होने के नाते System.Int32, इस तरह के एक दृश्य को इस आवश्यकता के विपरीत माना जाता है कि एक कोड एक मंच पर चलता है जिसकी कल्पना मूल्यों और वस्तुओं को विभिन्न ब्रह्मांडों का निवास मानती है। इसके अलावा, यदि Tएक संदर्भ प्रकार है, और xएक है T, तो प्रकार का एक संदर्भ Tसंदर्भित करने में सक्षम होना चाहिए x। इस प्रकार, यदि एक vप्रकार Int32का चर एक धारण करता है Object, तो प्रकार का Objectएक संदर्भ vया उसकी सामग्री के संदर्भ में सक्षम होना चाहिए । वास्तव में, प्रकार का एक संदर्भ Objectडेटा से कॉपी किए गए किसी ऑब्जेक्ट को इंगित करने में सक्षम होगा v, लेकिन vस्वयं को और न ही इसकी सामग्री को। यह सुझाव देगा कि न तोvऔर न ही इसकी सामग्री वास्तव में एक है Object


1
the only implicit conversion which would satisfy an overload of the Equals method would be the conversion to the object type corresponding to intगलत। जावा के विपरीत, C # में अलग-अलग आदिम और बॉक्सिंग प्रकार नहीं हैं। यह बॉक्सिंग की जा रही है objectक्योंकि यह केवल अन्य ओवरलोड है Equals()
SLAKs

पहला और तीसरा प्रश्न समान हैं; सटीक मान पहले ही रूपांतरण में खो गया था float। वसीयत के floatलिए कास्टिंग करना doubleजादुई रूप से नई परिशुद्धता पैदा नहीं करेगा।
SLKs

@SLaks: ECMA कल्पना के अनुसार, जो वर्चुअल मशीन का वर्णन करता है जिस पर C # चलता है, प्रत्येक मान प्रकार परिभाषा दो अलग-अलग प्रकार बनाती है। C # कल्पना कह सकती है कि प्रकार के भंडारण स्थान और प्रकार के List<String>.Enumeratorढेर ऑब्जेक्ट की सामग्री List<String>.Enumeratorसमान हैं, लेकिन ECMA / CLI कल्पना कहती है कि वे अलग हैं, और यहां तक ​​कि जब C # में उपयोग किया जाता है तो वे अलग तरह से व्यवहार करते हैं।
सुपरकैट

@SLaks: यदि iऔर fप्रत्येक में परिवर्तित किया गया doubleतुलना से पहले, वे १६७७७२१७.० और १,६७,७७,२१६.०, जो असमान रूप में की तुलना प्राप्त करेगी। परिवर्तित i floatकरने की तुलना में 16777216.0f का रूपांतरण होगा f
सुपरकैट

@ एसएलएसी: स्टोरेज-लोकेशन प्रकारों और बॉक्सिंग ऑब्जेक्ट प्रकारों के बीच अंतर के सरल उदाहरण के लिए, विधि पर विचार करें bool SelfSame<T>(T p) { return Object.ReferenceEquals((Object)p,(Object)p);}। एक मूल्य प्रकार के अनुरूप बॉक्सिंग ऑब्जेक्ट प्रकार ReferenceEqualsएक पहचान को बनाए रखने के माध्यम से पैरामीटर प्रकार को संतुष्ट कर सकता है ; भंडारण स्थान प्रकार, हालांकि, गैर-पहचान-संरक्षण रूपांतरण की आवश्यकता होती है । यदि मूल के अलावा किसी अन्य चीज़ का संदर्भ देने के Tलिए कोई कास्टिंग Uकरता है T, तो यह मुझे सुझाव देगा कि Tवास्तव में नहीं है U
सुपरकैट

5

बराबर () की एक विधि है System.Object कक्षा
सिंटेक्स: लोक आभासी bool बराबर ()
सिफ़ारिश अगर हम दो वस्तुओं की राज्य की तुलना करना चाहते हैं तो हम का उपयोग करना चाहिए बराबर () विधि

जैसा कि उत्तर ऊपर कहा गया है == ऑपरेटर तुलना करते हैं मान समान हैं।

कृपया ReferenceEqual से भ्रमित न हों

संदर्भ बराबर ()
सिंटैक्स: सार्वजनिक स्थैतिक बूल संदर्भ संदर्भ ()
यह निर्धारित करता है कि निर्दिष्ट ऑब्जेक्ट उदाहरण एक ही उदाहरण के हैं


8
इस सवाल का जवाब बिल्कुल नहीं है।
स्लाक्स

SLaks i dnt उदाहरणों के साथ समझाया गया है जो उपरोक्त प्रश्न के मूल हैं।
सुगत मनकर

4

आपको यह महसूस करने की आवश्यकता है कि क्या ==करना हमेशा एक विधि को कॉल करना होगा। सवाल यह है कि क्या कॉलिंग ==औरEquals समाप्त होती है / एक ही काम कर रही है।

संदर्भ प्रकारों के साथ, ==हमेशा 1 जांच करेंगे कि क्या संदर्भ समान हैं ( Object.ReferenceEquals)।Equalsदूसरी ओर ओवरराइड किया जा सकता है और यह जांच सकता है कि कुछ मूल्य समान हैं या नहीं।

EDIT: svick का जवाब देने और SLaks टिप्पणी पर जोड़ने के लिए, यहाँ कुछ IL कोड है

int i1 = 0x22; // ldc.i4.s ie pushes an int32 on the stack
int i2 = 0x33; // ldc.i4.s 
short s1 = 0x11; // ldc.i4.s (same as for int32)
short s2 = 0x22; // ldc.i4.s 

s1 == i1 // ceq
i1 == s1 // ceq
i1 == i2 // ceq
s1 == s2 // ceq
// no difference between int and short for those 4 cases,
// anyway the shorts are pushed as integers.

i1.Equals(i2) // calls System.Int32.Equals
s1.Equals(s2) // calls System.Int16.Equals
i1.Equals(s1) // calls System.Int32.Equals: s1 is considered as an integer
// - again it was pushed as such on the stack)
s1.Equals(i1) // boxes the int32 then calls System.Int16.Equals
// - int16 has 2 Equals methods: one for in16 and one for Object.
// Casting an int32 into an int16 is not safe, so the Object overload
// must be used instead.

तो क्या विधि int== कॉल के साथ दो एस की तुलना करता है ? संकेत: के लिए कोई operator ==विधि नहीं है Int32, लेकिन इसके लिए एक हैString
svick

2
यह प्रश्न का उत्तर नहीं देता है।
स्लाक्स

@ एसएलएसीएस: यह वास्तव में इंट और शॉर्ट तुलना के बारे में विशिष्ट प्रश्न का उत्तर नहीं देता है, आपने पहले ही इसका जवाब दे दिया है। मुझे अब भी यह समझाना दिलचस्प है कि ==यह केवल जादू नहीं करता है, यह अंततः एक विधि कहता है (अधिकांश प्रोग्रामर शायद कभी लागू नहीं होते हैं / किसी भी ऑपरेटर को ओवरराइड नहीं करते हैं)। हो सकता है कि मैं अपना उत्तर जोड़ने के बजाय आपके प्रश्न पर टिप्पणी जोड़ सकूं। यदि आपको लगता है कि जो मैंने कहा है वह प्रासंगिक है तो उसे अद्यतन करने के लिए स्वतंत्र महसूस करें।
user276648

ध्यान दें कि ==आदिम प्रकार पर एक अतिभारित ऑपरेटर नहीं है, लेकिन एक आंतरिक भाषा सुविधा है जो ceqआईएल निर्देश के लिए संकलित है ।
SLaks

3

== आदिम में

Console.WriteLine(age == newAge);          // true

आदिम तुलना में == ऑपरेटर काफी स्पष्ट व्यवहार करता है, C # में कई == ऑपरेटर ओवरलोड उपलब्ध हैं।

  • string == स्ट्रिंग
  • int == इंट
  • uint == यूइंट
  • लंबा == लंबा
  • बहुत अधिक

तो इस मामले में से कोई अंतर्निहित रूपांतरण नहीं intहैshort , लेकिन shortकरने के लिए intसंभव है। इसलिए न्यूएज को इंट में बदल दिया जाता है और तुलना होती है जो दोनों के लिए समान मूल्य रखता है। तो यह बराबर है:

Console.WriteLine(age == (int)newAge);          // true

.Equals () आदिम में

Console.WriteLine(newAge.Equals(age));         //false

यहां हमें यह देखने की जरूरत है कि समान () विधि क्या है, हम एक छोटे प्रकार के चर के साथ समान कहते हैं। तो तीन संभावनाएं हैं:

  • बराबर (वस्तु, वस्तु) // वस्तु से स्थिर विधि
  • बराबर (वस्तु) // वस्तु से आभासी विधि
  • बराबर (छोटा) // इम्प्लीमेंट्स IEquitable.Equals (छोटा)

पहला प्रकार यहाँ मामला नहीं है क्योंकि तर्कों की संख्या भिन्न है जिसे हम केवल प्रकार के तर्क के साथ बुला रहे हैं। तृतीय को भी समाप्त कर दिया गया है जैसा कि संक्षेप में int के निहितार्थ रूपांतरण से ऊपर संभव नहीं है। तो यहाँ पर दूसरे प्रकार Equals(object)को कहा जाता है। short.Equals(object)है:

bool Equals(object z)
{
  return z is short && (short)z == this;
}

तो यहाँ हालत का परीक्षण किया गया है z is shortजो z के रूप में गलत है एक int है इसलिए यह गलत है।

यहाँ एरिक लिपर्ट का विस्तृत लेख है

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