c# - हमें सी#में== और!=दोनों को परिभाषित क्यों करना चाहिए?




language-design (9)

सी # कंपाइलर की आवश्यकता है कि जब भी कोई कस्टम प्रकार ऑपरेटर == को परिभाषित करता है, तो इसे भी परिभाषित करना होगा != ( here देखें)।

क्यूं कर?

मुझे यह जानकर उत्सुकता है कि डिजाइनरों ने इसे क्यों जरूरी माना और क्यों कंपेलर ऑपरेटरों के लिए उचित कार्यान्वयन के लिए डिफ़ॉल्ट नहीं हो सकता है जब केवल दूसरा मौजूद होता है। उदाहरण के लिए, लुआ आपको केवल समानता ऑपरेटर को परिभाषित करने देता है और आप दूसरे को मुफ्त में प्राप्त करते हैं। सी # आपको या तो == या दोनों == और! = को परिभाषित करने के लिए कहकर ऐसा कर सकता है और फिर स्वचालित रूप से अनुपलब्ध! = ऑपरेटर को संकलित कर सकता है !(left == right)

मैं समझता हूं कि अजीब कोने के मामले हैं जहां कुछ संस्थाएं न तो बराबर और असमान हो सकती हैं, (आईईईई -754 नाएन की तरह), लेकिन वे अपवाद की तरह लगते हैं, नियम नहीं। तो यह स्पष्ट नहीं करता है कि सी # कंपाइलर डिजाइनरों ने अपवाद को नियम क्यों बनाया।

मैंने खराब कारीगरी के मामलों को देखा है जहां समानता ऑपरेटर परिभाषित किया गया है, तो असमानता ऑपरेटर एक प्रति-पेस्ट है जिसमें प्रत्येक तुलना उलट जाती है और प्रत्येक && को स्विच किया जाता है || (आपको बिंदु मिल जाता है ... मूल रूप से! (ए == बी) डी मॉर्गन के नियमों के माध्यम से विस्तारित)। यह खराब अभ्यास है कि संकलक डिजाइन द्वारा खत्म कर सकता है, जैसा लुआ के मामले में है।

नोट: ऑपरेटर के लिए वही है <> <=> =। मैं ऐसे मामलों की कल्पना नहीं कर सकता जहां आपको इन्हें अप्राकृतिक तरीकों से परिभाषित करने की आवश्यकता होगी। लुआ आपको केवल <और <= परिभाषित करता है> = और> स्वाभाविक रूप से फॉर्मर्स की अस्वीकृति के माध्यम से परिभाषित करता है। सी # ऐसा क्यों नहीं करता है (कम से कम 'डिफ़ॉल्ट रूप से')?

संपादित करें

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

हालांकि, मेरे प्रश्न का कर्नेल यही कारण है कि सी # में जबरन जरूरी है जब आमतौर पर यह तर्कसंगत रूप से आवश्यक नहीं है?

यह Object.Equals , IEquatable.Equals जैसे .NET इंटरफ़ेस के लिए डिज़ाइन विकल्पों के लिए हड़ताली विपरीत है। IEquatable.Equals IEqualityComparer.Equals जहां NotEquals counterpart की कमी से पता चलता है कि फ्रेमवर्क मानता है !Equals() ऑब्जेक्ट असमान के रूप में और वह है। इसके अलावा, Dictionary और विधियों जैसे वर्ग। .Contains() उपर्युक्त इंटरफेस पर विशेष रूप से निर्भर करते हैं और ऑपरेटरों को सीधे परिभाषित किए जाने पर भी इसका उपयोग नहीं करते हैं। वास्तव में, जब ReSharper समानता सदस्यों को उत्पन्न करता है, तो यह Equals() संदर्भ में == और != दोनों को परिभाषित करता है और तब भी जब उपयोगकर्ता ऑपरेटरों को उत्पन्न करने का विकल्प चुनता है। ऑब्जेक्ट समानता को समझने के लिए ढांचे द्वारा समानता ऑपरेटरों की आवश्यकता नहीं होती है।

असल में, .NET ढांचे इन ऑपरेटरों के बारे में परवाह नहीं करता है, यह केवल कुछ Equals विधियों की परवाह करता है। उपयोगकर्ता द्वारा टंडेम में परिभाषित करने के लिए == और! = ऑपरेटरों दोनों की आवश्यकता का निर्णय पूरी तरह से भाषा डिज़ाइन से संबंधित है और जहां तक ​​.NET का संबंध है, अर्थशास्त्र को ऑब्जेक्ट नहीं किया जाता है।


अपने संपादन का जवाब देने के लिए, यदि आप एक को ओवरराइड करते हैं तो दोनों को ओवरराइड करने के लिए मजबूर क्यों किया जाता है, यह सब विरासत में है।

यदि आप == ओवरराइड करते हैं, तो कुछ प्रकार की अर्थपूर्ण या संरचनात्मक समानता प्रदान करने की संभावना है (उदाहरण के लिए, डेटटाइम बराबर हैं यदि उनके आंतरिकटिक्स गुण समान हैं, तो वे अलग-अलग उदाहरण हो सकते हैं), तो आप ऑपरेटर का डिफ़ॉल्ट व्यवहार बदल रहे हैं ऑब्जेक्ट, जो सभी .NET ऑब्जेक्ट्स का अभिभावक है। == ऑपरेटर, सी # में एक विधि है, जिसका बेस कार्यान्वयन ऑब्जेक्ट.ऑपरेटर (==) एक रेफरेंशियल तुलना करता है। ऑब्जेक्ट.ऑपरेटर (! =) एक और, अलग विधि है, जो एक रेफरेंशियल तुलना भी करता है।

विधि ओवरराइडिंग के लगभग किसी अन्य मामले में, यह मानना ​​अजीब होगा कि एक विधि को ओवरराइड करने से एक एंटोनिमिक विधि में एक व्यवहारिक परिवर्तन भी हो सकता है। यदि आपने वृद्धि कक्षा () और कमीशन () विधियों के साथ एक वर्ग बनाया है, और एक बच्चे वर्ग में वृद्धि () को ओवरराइड किया है, तो क्या आप अपने ओवरराइड व्यवहार के विपरीत विचलन () को ओवरराइड करने की अपेक्षा करेंगे? संकलक को सभी संभावित मामलों में ऑपरेटर के किसी भी कार्यान्वयन के लिए एक व्यस्त कार्य उत्पन्न करने के लिए पर्याप्त स्मार्ट नहीं बनाया जा सकता है।

हालांकि, ऑपरेटर, हालांकि विधियों के समान ही लागू होते हैं, अवधारणाओं में जोड़ों में काम करते हैं; == और! =, <और>, और <= और> =। उपभोक्ता के दृष्टिकोण से इस मामले में यह अजीब होगा कि यह सोचने के लिए! = = == से अलग तरीके से काम करता है। तो, संकलक को यह मानने के लिए नहीं बनाया जा सकता है कि एक! = B ==! (A == b) सभी मामलों में, लेकिन आमतौर पर यह अपेक्षा की जाती है कि == और! = एक समान फैशन में काम करना चाहिए, इसलिए कंपाइलर बलों आप जोड़ों में लागू करने के लिए, हालांकि आप वास्तव में ऐसा करने के अंत में। यदि, आपकी कक्षा के लिए, ए! = बी ==! (ए == बी), तो बस! = ऑपरेटर का उपयोग करके! = ==) को लागू करें, लेकिन यदि वह नियम आपके ऑब्जेक्ट के लिए सभी मामलों में नहीं है (उदाहरण के लिए , यदि किसी विशेष मान के साथ तुलना, बराबर या असमान, वैध नहीं है), तो आपको आईडीई से अधिक स्मार्ट होना चाहिए।

वास्तविक प्रश्न पूछा जाना चाहिए कि क्यों <और> और <= और> = तुलनात्मक ऑपरेटरों के लिए जोड़े हैं जिन्हें समवर्ती रूप से कार्यान्वित किया जाना चाहिए, जब संख्यात्मक शब्दों में! (A <b) == a> = b और! (A> बी) == एक <= बी। यदि आप एक को ओवरराइड करते हैं तो आपको सभी चार को लागू करने की आवश्यकता होनी चाहिए, और आपको शायद == (और! =) को ओवरराइड करने की आवश्यकता होनी चाहिए, क्योंकि (एक <= b) == (a == b) यदि कोई अर्थात् रूप से है बी के बराबर


आपके प्रश्न में मुख्य शब्द " क्यों " और " जरूरी " हैं।

नतीजतन:

इसका उत्तर इस तरह से किया गया है क्योंकि उन्होंने इसे ऐसा करने के लिए डिजाइन किया है, सच है ... लेकिन आपके प्रश्न के "क्यों" भाग का जवाब नहीं दे रहा है।

जवाब देते हुए कि कभी-कभी इन दोनों को स्वतंत्र रूप से ओवरराइड करने में मदद मिल सकती है, सच है ... लेकिन आपके प्रश्न के "जरूरी" हिस्से का जवाब नहीं दे रहा है।

मुझे लगता है कि सरल जवाब यह है कि कोई ठोस कारण नहीं है कि सी # के लिए आपको दोनों को ओवरराइड करने की आवश्यकता क्यों है।

भाषा आपको केवल == को ओवरराइड करने की अनुमति देगी, और आपको एक डिफ़ॉल्ट कार्यान्वयन प्रदान करेगी != वह है ! उस। यदि आप ओवरराइड करना चाहते हैं != साथ ही, इसमें है।

यह एक अच्छा फैसला नहीं था। मनुष्य डिजाइन भाषाएं, इंसान सही नहीं हैं, सी # सही नहीं है। श्राग और क्यूईडी


प्रोग्रामिंग भाषा असाधारण जटिल तार्किक बयान के वाक्य रचनात्मक पुनर्गठन हैं। इस बात को ध्यान में रखते हुए, क्या आप समानता के मामले को परिभाषित किए बिना समानता के मामले को परिभाषित कर सकते हैं? जवाब न है। किसी ऑब्जेक्ट के लिए ऑब्जेक्ट बी के बराबर होना चाहिए, फिर ऑब्जेक्ट के विपरीत एक बराबर नहीं है बी भी सत्य होना चाहिए। यह दिखाने का एक और तरीका है

if a == b then !(a != b)

यह वस्तुओं की समानता निर्धारित करने के लिए भाषा के लिए निश्चित क्षमता प्रदान करता है। उदाहरण के लिए, तुलना NULL! = NULL एक समानता प्रणाली की परिभाषा में एक रिंच फेंक सकता है जो एक समानता बयान लागू नहीं करता है।

अब, के विचार के संबंध में! = बस प्रतिस्थापन योग्य परिभाषा के रूप में

if !(a==b) then a!=b

मैं इसके साथ बहस नहीं कर सकता। हालांकि, यह संभवतः सी # भाषा विनिर्देश समूह द्वारा एक निर्णय था कि प्रोग्रामर को एक वस्तु की समानता और गैर-समानता को स्पष्ट रूप से परिभाषित करने के लिए मजबूर होना चाहिए


मैं भाषा डिजाइनरों के लिए बात नहीं कर सकता, लेकिन जो भी मैं कर सकता हूं, ऐसा लगता है कि यह जानबूझकर, उचित डिजाइन निर्णय था।

इस मूल एफ # कोड को देखते हुए, आप इसे एक वर्किंग लाइब्रेरी में संकलित कर सकते हैं। यह एफ # के लिए कानूनी कोड है, और केवल समानता ऑपरेटर को अधिभारित करता है, असमानता नहीं:

module Module1

type Foo() =
    let mutable myInternalValue = 0
    member this.Prop
        with get () = myInternalValue
        and set (value) = myInternalValue <- value

    static member op_Equality (left : Foo, right : Foo) = left.Prop = right.Prop
    //static member op_Inequality (left : Foo, right : Foo) = left.Prop <> right.Prop

यह वही करता है जो ऐसा लगता है। यह केवल == पर समानता तुलनाकर्ता बनाता है, और यह देखने के लिए जांच करता है कि कक्षा के आंतरिक मान बराबर हैं या नहीं।

जबकि आप सी # में इस तरह की कक्षा नहीं बना सकते हैं, आप .NET के लिए संकलित किए गए एक का उपयोग कर सकते हैं। यह स्पष्ट है कि यह हमारे ओवरलोडेड ऑपरेटर का उपयोग == लिए करेगा, तो रनटाइम का उपयोग क्या होता है != ?

सी # ईएमसीए मानक में नियमों का एक पूरा समूह है (धारा 14.9) यह समझाता है कि समानता का मूल्यांकन करते समय किस ऑपरेटर का उपयोग करना है। इसे अत्यधिक सरलीकृत करने के लिए और इस प्रकार पूरी तरह से सटीक नहीं है, यदि तुलना की जा रही प्रकार एक ही प्रकार के हैं और एक अधिभारित समानता ऑपरेटर मौजूद है, तो यह उस अधिभार का उपयोग करेगा, न कि ऑब्जेक्ट से प्राप्त मानक संदर्भ समानता ऑपरेटर। यह आश्चर्य की बात नहीं है कि, यदि ऑपरेटरों में से केवल एक मौजूद है, तो यह डिफ़ॉल्ट संदर्भ समानता ऑपरेटर का उपयोग करेगा, कि सभी ऑब्जेक्ट्स के लिए, इसके लिए अधिभार नहीं है। 1

यह जानकर कि यह मामला है, असली सवाल यह है कि: इस तरह से यह क्यों डिजाइन किया गया था और संकलक इसे अपने आप क्यों नहीं समझता? बहुत से लोग कह रहे हैं कि यह एक डिज़ाइन निर्णय नहीं था, लेकिन मुझे लगता है कि यह इस तरह से सोचा गया था, खासकर इस तथ्य के बारे में कि सभी ऑब्जेक्ट्स में डिफ़ॉल्ट समानता ऑपरेटर है।

तो, संकलक स्वचालित रूप से != ऑपरेटर क्यों नहीं बनाते? मैं निश्चित रूप से तब तक नहीं जान सकता जब तक कि माइक्रोसॉफ्ट के किसी ने इसकी पुष्टि नहीं की, लेकिन तथ्यों पर तर्क से मैं यह निर्धारित कर सकता हूं।

अप्रत्याशित व्यवहार को रोकने के लिए

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

इसे डिफ़ॉल्ट रूप से जाने के बजाय, इसे अधिभारित करने के लिए मजबूर करने के मामले में, मैं केवल दृढ़ता से कह सकता हूं कि यह मानक (ईएमसीए -334 17.9.2) 2 में है । मानक निर्दिष्ट नहीं करता है क्यों। मेरा मानना ​​है कि यह इस तथ्य के कारण है कि सी # सी ++ से ज्यादा व्यवहार करता है। इस पर और अधिक के लिए नीचे देखें।

जब आप ओवरराइड करते हैं != और == , आपको बूल वापस करने की ज़रूरत नहीं है।

यह एक और संभावित कारण है। सी # में, यह कार्य:

public static int operator ==(MyClass a, MyClass b) { return 0; }

इस के रूप में मान्य है:

public static bool operator ==(MyClass a, MyClass b) { return true; }

यदि आप बूल के अलावा कुछ और लौट रहे हैं, तो संकलक स्वचालित रूप से विपरीत प्रकार का अनुमान नहीं लगा सकता है । इसके अलावा, जहां आपका ऑपरेटर बूल लौटाता है, तो यह उनके लिए जेनरेट कोड उत्पन्न नहीं करता है जो केवल उस विशिष्ट मामले में मौजूद होगा या जैसा कि मैंने ऊपर कहा है, कोड जो सीएलआर के डिफ़ॉल्ट व्यवहार को छुपाता है।

सी #+ सी ​​++ 3 से ज्यादा उधार लेता है

जब सी # पेश किया गया था, एमएसडीएन पत्रिका में एक लेख लिखा था जो सी # के बारे में बात कर रहा था:

कई डेवलपर्स चाहते हैं कि वहां एक ऐसी भाषा थी जो विजुअल बेसिक की तरह लिखना, पढ़ना और बनाए रखना आसान था, लेकिन फिर भी उसने सी ++ की शक्ति और लचीलापन प्रदान किया।

हां सी # के लिए डिज़ाइन लक्ष्य सी ++ के रूप में लगभग उसी शक्ति को देना था, जो कठोर प्रकार-सुरक्षा और कचरा-संग्रह जैसी सुविधाओं के लिए केवल थोड़ी सी बलिदान देता था। सी #+ के बाद सी # दृढ़ता से मॉडलिंग किया गया था।

आप यह जानकर आश्चर्यचकित नहीं हो सकते कि सी ++ में, समानता ऑपरेटरों को बूल वापस करने की ज़रूरत नहीं है , जैसा कि इस उदाहरण कार्यक्रम में दिखाया गया है

अब, सी ++ आपको पूरक ऑपरेटर को अधिभारित करने की आवश्यकता नहीं है। यदि आपने उदाहरण प्रोग्राम में कोड संकलित किया है, तो आप देखेंगे कि यह कोई त्रुटि नहीं है। हालांकि, यदि आपने लाइन जोड़ने का प्रयास किया है:

cout << (a != b);

तुम्हे मिल जाएगा

कंपाइलर त्रुटि C2678 (एमएसवीसी): बाइनरी '! =': कोई ऑपरेटर नहीं मिला जो 'टेस्ट' प्रकार का बाएं हाथ का ऑपरेंड लेता है (या कोई स्वीकार्य रूपांतरण नहीं है)।

इसलिए, जबकि सी ++ को आपको जोड़ों में अधिभारित करने की आवश्यकता नहीं है, यह आपको एक समानता ऑपरेटर का उपयोग नहीं करने देगा जो आपने कस्टम क्लास पर ओवरलोड नहीं किया है। यह .NET में मान्य है, क्योंकि सभी ऑब्जेक्ट्स में डिफ़ॉल्ट है; सी ++ नहीं करता है।

1. एक साइड नोट के रूप में, सी # मानक के लिए अभी भी आपको ऑपरेटरों की जोड़ी अधिभारित करने की आवश्यकता है यदि आप किसी एक को अधिभारित करना चाहते हैं। यह मानक का हिस्सा है और न केवल संकलक हालांकि, जब आप किसी अन्य भाषा में लिखी गई .net लाइब्रेरी तक पहुंच रहे हों तो उस ऑपरेटर को कॉल करने के निर्धारण के संबंध में एक ही नियम लागू होते हैं, जिसमें समान आवश्यकताएं नहीं होती हैं।

2. ईएमसीए -334 (पीडीएफ) ( http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-334.pdf )

3. और जावा, लेकिन यह वास्तव में यहां बिंदु नहीं है


यदि आप अपने कस्टम प्रकार के लिए == ओवरलोड करते हैं, और नहीं! = तो ऑब्जेक्ट के लिए! = ऑपरेटर द्वारा इसे संभाला जाएगा! = ऑब्जेक्ट क्योंकि ऑब्जेक्ट से सबकुछ व्युत्पन्न होता है, और यह कस्टम टाइप से बहुत अलग होगा! = कस्टम टाइप।

इसके अलावा भाषा निर्माता शायद इसे कोडर के लिए सबसे अधिक लचीलापन की अनुमति देने के लिए चाहते थे, और यह भी कि वे आपके द्वारा किए जाने वाले कार्यों के बारे में धारणा नहीं बना रहे हैं।


यह मेरे दिमाग में पहले आता है:

  • यदि परीक्षण असमानता समानता परीक्षण की तुलना में बहुत तेज है तो क्या होगा ?
  • क्या होगा यदि कुछ मामलों में आप == और != लिए false वापसी करना चाहते हैं (यानी अगर उन्हें किसी कारण से तुलना नहीं की जा सकती है)

शायद अगर किसी को तीन मूल्यवान तर्क (यानी null ) लागू करने की आवश्यकता है। इस तरह के मामलों में - उदाहरण के लिए एएनएसआई मानक एसक्यूएल - ऑपरेटरों को इनपुट के आधार पर केवल नकारात्मक नहीं किया जा सकता है।

आपके पास एक मामला हो सकता है जहां:

var a = SomeObject();

और a == true रिटर्न false और a == false भी false वापसी करता false


शायद कुछ ऐसा जो उन्होंने नहीं सोचा था, उसके पास समय नहीं था।

जब मैं ओवरलोड == करता हूं तो मैं हमेशा आपकी विधि का उपयोग करता हूं। तब मैं इसे दूसरे में इस्तेमाल करता हूं।

आप सही काम के साथ सही हैं, संकलक हमें मुफ्त में दे सकता है।


सी # के अलावा कई क्षेत्रों में सी ++ को रोकता है, सबसे अच्छा स्पष्टीकरण मैं सोच सकता हूं कि कुछ मामलों में आप "समानता" साबित करने के बजाय "समानता नहीं" साबित करने के लिए थोड़ा अलग दृष्टिकोण लेना चाहेंगे।

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







language-design