डबल के लिए "==" ऑपरेटर की परिभाषा


126

किसी कारण से मैं वर्ग के लिए .NET फ्रेमवर्क स्रोत में घुस रहा था Doubleऔर पता चला कि इस की घोषणा ==है:

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

प्रत्येक ऑपरेटर के लिए एक ही तर्क लागू होता है ।


  • ऐसी परिभाषा की क्या बात है?
  • यह कैसे काम करता है?
  • यह एक अनंत पुनरावृत्ति क्यों नहीं बनाता है?

17
मैं एक अंतहीन पुनरावृत्ति की उम्मीद है।
१५:३० पर HimBromBeere

5
मुझे पूरा यकीन है कि इसे डबल के साथ कहीं भी तुलना के लिए उपयोग नहीं किया जाता है, इसके बजाय ceqआईएल में जारी किया जाता है। यह सिर्फ कुछ प्रलेखन उद्देश्य को भरने के लिए है, हालांकि स्रोत नहीं मिल सकता है।
हबीब

2
सबसे अधिक संभावना है कि इस ऑपरेटर को प्रतिबिंब के माध्यम से प्राप्त किया जा सकता है।
Damien_The_Unbeliever

3
यह कभी नहीं कहा जाएगा, संकलक समानता तर्क (Ceq opcode) में पके हुए है देखें कब डबल == है?
एलेक्स के।

1
@ ZoharPeled शून्य के साथ एक डबल को विभाजित करना मान्य है और इसके परिणामस्वरूप सकारात्मक या नकारात्मक अनंतता होगी।
मैग्नस

जवाबों:


62

वास्तव में, कंपाइलर ==ऑपरेटर को ceqIL कोड में बदल देगा , और आपके द्वारा उल्लेखित ऑपरेटर को कॉल नहीं किया जाएगा।

स्रोत कोड में ऑपरेटर के लिए कारण होने की संभावना है, इसलिए इसे C # के अलावा अन्य भाषाओं से कॉल किया जा सकता है जो इसे CEQसीधे कॉल में अनुवाद नहीं करते (या प्रतिबिंब के माध्यम से)। भीतर कोडऑपरेटर संकलित किया जाएगाCEQ , ताकि कोई अनंत पुनरावृत्ति न हो।

वास्तव में, यदि आप ऑपरेटर को प्रतिबिंब के माध्यम से बुलाते हैं, तो आप देख सकते हैं कि ऑपरेटर को बुलाया जाता है (बजाय ए CEQ निर्देश के ) , और स्पष्ट रूप से असीम रूप से पुनरावर्ती नहीं है (चूंकि कार्यक्रम अपेक्षित रूप से समाप्त होता है):

double d1 = 1.1;
double d2 = 2.2;

MethodInfo mi = typeof(Double).GetMethod("op_Equality", BindingFlags.Static | BindingFlags.Public );

bool b = (bool)(mi.Invoke(null, new object[] {d1,d2}));

परिणामी IL (LinqPad 4 द्वारा संकलित):

IL_0000:  nop         
IL_0001:  ldc.r8      9A 99 99 99 99 99 F1 3F 
IL_000A:  stloc.0     // d1
IL_000B:  ldc.r8      9A 99 99 99 99 99 01 40 
IL_0014:  stloc.1     // d2
IL_0015:  ldtoken     System.Double
IL_001A:  call        System.Type.GetTypeFromHandle
IL_001F:  ldstr       "op_Equality"
IL_0024:  ldc.i4.s    18 
IL_0026:  call        System.Type.GetMethod
IL_002B:  stloc.2     // mi
IL_002C:  ldloc.2     // mi
IL_002D:  ldnull      
IL_002E:  ldc.i4.2    
IL_002F:  newarr      System.Object
IL_0034:  stloc.s     04 // CS$0$0000
IL_0036:  ldloc.s     04 // CS$0$0000
IL_0038:  ldc.i4.0    
IL_0039:  ldloc.0     // d1
IL_003A:  box         System.Double
IL_003F:  stelem.ref  
IL_0040:  ldloc.s     04 // CS$0$0000
IL_0042:  ldc.i4.1    
IL_0043:  ldloc.1     // d2
IL_0044:  box         System.Double
IL_0049:  stelem.ref  
IL_004A:  ldloc.s     04 // CS$0$0000
IL_004C:  callvirt    System.Reflection.MethodBase.Invoke
IL_0051:  unbox.any   System.Boolean
IL_0056:  stloc.3     // b
IL_0057:  ret 

दिलचस्प बात यह है - एक ही ऑपरेटरों अभिन्न प्रकार के लिए मौजूद नहीं है (या तो संदर्भ स्रोत प्रतिबिंब में या माध्यम से), केवल Single, Double, Decimal, String, और DateTime, मेरे सिद्धांत disproves है कि वे अन्य भाषाओं से कहा जा लिए मौजूद हैं। जाहिर है आप इन ऑपरेटरों के बिना अन्य भाषाओं में दो पूर्णांकों की बराबरी कर सकते हैं, इसलिए हम इस सवाल पर वापस आ गए कि "वे क्यों मौजूद हैं double"?


12
इसके साथ मैं केवल एक ही समस्या देख सकता हूं कि सी # भाषा विनिर्देश कहता है कि ओवरलोडेड ऑपरेटर बिल्ट-इन ऑपरेटरों पर पूर्वता लेते हैं। तो निश्चित रूप से, एक अनुरूप सी # संकलक को यह देखना चाहिए कि एक अतिभारित ऑपरेटर यहां उपलब्ध है और अनंत पुनरावृत्ति उत्पन्न करता है। हम्म। परेशान कर रहा।
Damien_The_Unbeliever

5
इस सवाल का जवाब नहीं है, imho। यह केवल यह बताता है कि कोड का अनुवाद क्या है लेकिन क्यों नहीं। खंड #.३.४ बाइनरी ऑपरेटर ओवरलोड रिज़ॉल्यूशन के अनुसार सी # भाषा विनिर्देश मैं भी अनंत पुनरावृत्ति की उम्मीद करेगा। मुझे लगता है कि संदर्भ स्रोत ( Referenceource.microsoft.com/#mscorlib/system/… ) वास्तव में यहां लागू नहीं होता है।
डर्क वोल्मार

6
@DStanley - मैं उत्पादित नहीं कर रहा हूँ। मैं कह रहा हूं कि मैं इसे भाषा की युक्ति के साथ नहीं समेट सकता। यही तो परेशान कर रहा है। मैं रोजलिन के माध्यम से विचार करने के बारे में सोच रहा था और देख रहा था कि क्या मुझे यहां कोई विशेष हैंडलिंग मिल सकती है लेकिन मैं वर्तमान में (गलत मशीन) ऐसा करने के लिए तैयार नहीं हूं
Damien_The_Unbeliever

1
@Damien_The_Unbeliever यही कारण है कि मुझे लगता है कि यह या तो कल्पना का अपवाद है या "अंतर्निर्मित" ऑपरेटरों की एक अलग व्याख्या है।
डी स्टेनली

1
जैसा कि @ जोंन स्कीट ने अभी तक उत्तर नहीं दिया है, या इस पर टिप्पणी नहीं की है, मुझे संदेह है कि यह एक बग है (यानी कल्पना का उल्लंघन)।
TheBlastOne

37

यहाँ मुख्य भ्रम यह है कि आप मान रहे हैं कि सभी .NET लाइब्रेरीज़ (इस मामले में, विस्तारित न्यूमेरिक्स लाइब्रेरी, जो BCL का हिस्सा नहीं है) मानक C # में लिखी गई हैं। यह हमेशा ऐसा नहीं होता है, और विभिन्न भाषाओं में अलग-अलग नियम होते हैं।

मानक C # में, जिस कोड को आप देख रहे हैं, उसके परिणामस्वरूप ऑपरेटर ओवरलोड रिज़ॉल्यूशन काम करने के तरीके के कारण स्टैक ओवरफ़्लो होगा। हालाँकि, कोड वास्तव में मानक C # में नहीं है - यह मूल रूप से C # संकलक की अनिर्दिष्ट सुविधाओं का उपयोग करता है। ऑपरेटर को कॉल करने के बजाय, यह इस कोड का उत्सर्जन करता है:

ldarg.0
ldarg.1
ceq
ret

यह बात है :) कोई 100% समतुल्य सी # कोड नहीं है - यह आपके स्वयं के प्रकार के साथ सी # में संभव नहीं है ।

फिर भी, C # कोड का संकलन करते समय वास्तविक ऑपरेटर का उपयोग नहीं किया जाता है - कंपाइलर अनुकूलन का एक गुच्छा करता है, जैसे कि इस मामले में, जहां यह op_Equalityकॉल को सरल के साथ बदल देता है ceq। फिर, आप इसे अपने में नहीं दोहरा सकतेDoubleEx संरचना - यह संकलक जादू है।

यह निश्चित रूप से .NET में एक अनोखी स्थिति नहीं है - बहुत सारे कोड हैं जो मान्य नहीं हैं, मानक C #। कारण आम तौर पर (ए) कंपाइलर हैक्स और (बी) एक अलग भाषा होते हैं, विषम (सी) रनटाइम हैक्स के साथ (मैं आपको देख रहा हूं,Nullable !)।

चूंकि रोसलिन सी # कंपाइलर ओप्पन स्रोत है, मैं वास्तव में आपको उस स्थान पर इंगित कर सकता हूं जहां अधिभार संकल्प तय किया गया है:

वह स्थान जहाँ सभी बाइनरी ऑपरेटरों को हल किया जाता है

आंतरिक ऑपरेटरों के लिए "शॉर्टकट"

जब आप शॉर्टकट देखते हैं, तो आप देखेंगे कि आंतरिक डबल ऑपरेटर में दोहरे और दोहरे परिणामों के बीच समानता, कभी भी वास्तविक ==ऑपरेटर के प्रकार पर परिभाषित नहीं होती है। .NET प्रकार की प्रणाली को दिखावा करना पड़ता है Doubleजो किसी भी अन्य की तरह है, लेकिन C # नहीं - doubleC # में एक आदिम है।


1
मुझे यकीन नहीं है कि मैं मानता हूं कि संदर्भ स्रोत में कोड सिर्फ "रिवर्स इंजीनियर" है। कोड में संकलक निर्देश ( #ifओं) और अन्य कलाकृतियाँ हैं जो संकलित कोड में मौजूद नहीं होंगी। प्लस अगर यह रिवर्स के लिए इंजीनियर थे doubleतो क्यों इसके लिए इंजीनियर रिवर्स नहीं किया गया था intया long? मुझे लगता है कि स्रोत कोड के लिए एक कारण है, लेकिन विश्वास है कि ==ऑपरेटर के अंदर का उपयोग संकलित CEQहोने से बचाता है। चूंकि ऑपरेटर उस प्रकार के लिए "पूर्वनिर्धारित" ऑपरेटर है (और इसे ओवरराइड नहीं किया जा सकता है) ओवरलोड नियम लागू नहीं होते हैं।
डी स्टेनली

@DStanley मैं नहीं चाहता था कि सभी कोड रिवर्स इंजीनियर हैं। और फिर, doubleबीसीएल का हिस्सा नहीं है - यह एक अलग पुस्तकालय में है, जो कि सी # विनिर्देश में शामिल होने के लिए होता है। हां, ==एक के लिए संकलित हो जाता है ceq, लेकिन इसका मतलब यह है कि यह एक कंपाइलर हैक है जिसे आप अपने कोड में दोहरा नहीं सकते हैं, और कुछ ऐसा जो सी # विनिर्देश का हिस्सा नहीं है (बस संरचना float64पर फ़ील्ड की तरह Double)। यह C # का एक संविदात्मक हिस्सा नहीं है, इसलिए इसे मान्य C # की तरह व्यवहार करने का कोई मतलब नहीं है, भले ही इसे C # संकलक के साथ संकलित किया गया हो।
लुआं

@ वास्तव में मैं यह नहीं जान पाया कि वास्तविक रूपरेखा कैसे व्यवस्थित की जाती है, लेकिन .NET 2.0 के संदर्भ कार्यान्वयन में, सभी मुश्किल भाग सिर्फ संकलक आंतरिक हैं, जिन्हें C ++ में लागू किया गया है। .NET नेट कोड अभी भी बहुत सारे हैं, लेकिन "दो डबल्स की तुलना" जैसी चीजें वास्तव में शुद्ध .NET में अच्छी तरह से काम नहीं करेंगी; यह एक कारण है कि फ्लोटिंग पॉइंट नंबर BCL में शामिल नहीं हैं। उस ने कहा, कोड को (गैर-मानक) C # में भी लागू किया गया है, संभवतः आपके द्वारा पहले बताए गए कारण के लिए - यह सुनिश्चित करने के लिए कि अन्य .NET कंपाइलर उन प्रकारों को वास्तविक .NET प्रकारों के रूप में मान सकते हैं।
लुआं

@DStanley लेकिन ठीक है, बिंदु लिया। मैंने "रिवर्स इंजीनियर" संदर्भ हटा दिया, और केवल C # के बजाय "मानक C #" का स्पष्ट रूप से उल्लेख करने के लिए उत्तर दिया। और doubleउसी तरह से व्यवहार न करें intऔर long- intऔर longआदिम प्रकार हैं जो सभी .NET भाषाओं को समर्थन करना चाहिए। float, decimalऔर doubleनहीं कर रहे हैं।
लुआं

12

आदिम प्रकार के स्रोत भ्रामक हो सकते हैं। क्या आपने Doubleसंरचना की पहली पंक्ति देखी है ?

आम तौर पर आप इस तरह एक पुनरावर्ती संरचना को परिभाषित नहीं कर सकते:

public struct Double : IComparable, IFormattable, IConvertible
        , IComparable<Double>, IEquatable<Double>
{
    internal double m_value; // Self-recursion with endless loop?
    // ...
}

CIL में आदिम प्रकारों को अपना मूल समर्थन है। आम तौर पर उनके साथ वस्तु-उन्मुख प्रकारों की तरह व्यवहार नहीं किया जाता है। एक डबल सिर्फ 64-बिट मान है यदि इसे उपयोग किया जाता हैfloat64 CIL के में । हालाँकि, यदि इसे एक सामान्य .NET प्रकार के रूप में संभाला जाता है, तो इसमें एक वास्तविक मूल्य होता है और इसमें किसी अन्य प्रकार की तरह विधियाँ शामिल होती हैं।

तो जो आप यहां देख रहे हैं वही ऑपरेटरों के लिए स्थिति है। आम तौर पर यदि आप सीधे दोहरे प्रकार के प्रकार का उपयोग करते हैं, तो इसे कभी नहीं कहा जाएगा। BTW, इसका स्रोत CIL में इस तरह दिखता है:

.method public hidebysig specialname static bool op_Equality(float64 left, float64 right) cil managed
{
    .custom instance void System.Runtime.Versioning.NonVersionableAttribute::.ctor()
    .custom instance void __DynamicallyInvokableAttribute::.ctor()
    .maxstack 8
    L_0000: ldarg.0
    L_0001: ldarg.1
    L_0002: ceq
    L_0004: ret
}

जैसा कि आप देख सकते हैं, कोई अंतहीन लूप नहीं है ( ceqउपकरण को कॉल करने के बजाय उपयोग किया जाता है System.Double::op_Equality)। इसलिए जब किसी वस्तु की तरह दोहरा व्यवहार किया जाता है, तो ऑपरेटर विधि को बुलाया जाएगा, जो अंततः इसे float64CIL स्तर पर आदिम प्रकार के रूप में संभाल लेगा ।


1
उन लोगों के लिए जो इस पोस्ट के पहले भाग को नहीं समझते हैं (शायद इसलिए कि वे आमतौर पर अपने स्वयं के मूल्य नहीं लिखते हैं), कोड का प्रयास करें public struct MyNumber { internal MyNumber m_value; }। यह संकलित नहीं किया जा सकता है, निश्चित रूप से। त्रुटि CS0523
जेपी स्टिग नीलसन

8

मैंने जस्टडेकम्पाइल के साथ CIL पर एक नज़र डाली। आंतरिक ==CIL Ceq में अनुवादित हो जाता है सेशन कोड में । दूसरे शब्दों में, यह आदिम सीएलआर समानता है।

मैं यह देखने के लिए उत्सुक था कि क्या सी # कंपाइलर दो डबल मानों की तुलना करते समय ऑपरेटर को संदर्भित करेगा ceqया नहीं ==। तुच्छ उदाहरण में मैं (नीचे) के साथ आया था, इसका इस्तेमाल कियाceq

यह कार्यक्रम:

void Main()
{
    double x = 1;
    double y = 2;

    if (x == y)
        Console.WriteLine("Something bad happened!");
    else
        Console.WriteLine("All is right with the world");
}

निम्नलिखित CIL उत्पन्न करता है (लेबल के साथ विवरण नोट करें IL_0017):

IL_0000:  nop
IL_0001:  ldc.r8      00 00 00 00 00 00 F0 3F
IL_000A:  stloc.0     // x
IL_000B:  ldc.r8      00 00 00 00 00 00 00 40
IL_0014:  stloc.1     // y
IL_0015:  ldloc.0     // x
IL_0016:  ldloc.1     // y
IL_0017:  ceq
IL_0019:  stloc.2
IL_001A:  ldloc.2
IL_001B:  brfalse.s   IL_002A
IL_001D:  ldstr       "Something bad happened!"
IL_0022:  call        System.Console.WriteLine
IL_0027:  nop
IL_0028:  br.s        IL_0035
IL_002A:  ldstr       "All is right with the world"
IL_002F:  call        System.Console.WriteLine
IL_0034:  nop
IL_0035:  ret

-2

जैसा कि System.Runtime.Versioning Namespace के लिए Microsoft दस्तावेज़ीकरण में दिखाया गया है: इस नामस्थान में पाए जाने वाले प्रकार .NET फ्रेमवर्क के भीतर उपयोग करने के लिए होते हैं न कि उपयोगकर्ता अनुप्रयोगों के लिए। System.Runtime.Versioning नामस्थान में उन्नत प्रकार शामिल हैं: समर्थन संस्करण में .NET फ्रेमवर्क के साइड इम्प्लीमेंटेशन।


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