लघु परिचालिका करें || और & Nullable बूलियन के लिए मौजूद है? RuntimeBinder कभी-कभी ऐसा सोचता है


84

मैंने सशर्त तार्किक संचालकों पर C # भाषा विनिर्देशन पढ़ा ||और &&, जिसे लघु-संचारी तार्किक संचालकों के रूप में भी जाना जाता है। अगर यह अशक्त बूलियन के लिए अस्तित्व में है, तो मुझे यह अस्पष्ट लग रहा था, अर्थात ऑपरेंड प्रकार Nullable<bool>(भी लिखा हुआ bool?), इसलिए मैंने इसे गैर-गतिशील टाइपिंग के साथ आज़माया:

bool a = true;
bool? b = null;
bool? xxxx = b || a;  // compile-time error, || can't be applied to these types

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

हालांकि, मैं dynamicबंधन के साथ भी कोशिश करना चाहता था । इसलिए मैंने इसके बजाय यह कोशिश की:

static class Program
{
  static dynamic A
  {
    get
    {
      Console.WriteLine("'A' evaluated");
      return true;
    }
  }
  static dynamic B
  {
    get
    {
      Console.WriteLine("'B' evaluated");
      return null;
    }
  }

  static void Main()
  {
    dynamic x = A | B;
    Console.WriteLine((object)x);
    dynamic y = A & B;
    Console.WriteLine((object)y);

    dynamic xx = A || B;
    Console.WriteLine((object)xx);
    dynamic yy = A && B;
    Console.WriteLine((object)yy);
  }
}

आश्चर्यजनक परिणाम यह है कि यह बिना किसी अपवाद के चलता है।

ठीक है, xऔर yआश्चर्य की बात नहीं है, उनकी घोषणाएं दोनों गुणों को पुनः प्राप्त करने की ओर ले जाती हैं, और परिणामस्वरूप मान अपेक्षित हैं, xहै trueऔर yहै null

लेकिन बिना किसी बाध्यकारी समय के अपवाद xxके A || Bनेतृत्व का मूल्यांकन , और केवल संपत्ति Aको पढ़ा गया था, नहीं B। क्यों होता है ऐसा? जैसा कि आप बता सकते हैं, हम Bएक पागल वस्तु को वापस करने के लिए गेट्टर बदल सकते हैं , जैसे "Hello world", और xxअभी भी trueबाध्यकारी-समस्याओं के बिना मूल्यांकन करेंगे ...

मूल्यांकन A && B(के लिए yy) भी कोई बाध्यकारी समय त्रुटि की ओर जाता है। और यहाँ दोनों गुणों को पुनः प्राप्त किया जाता है, निश्चित रूप से। रन-टाइम बाइंडर द्वारा इसकी अनुमति क्यों दी गई है? यदि दी गई वस्तु को B"खराब" ऑब्जेक्ट (जैसे string) में बदल दिया जाता है , तो एक बाध्यकारी अपवाद उत्पन्न होता है।

क्या यह सही व्यवहार है? (आप कल्पना से कैसे अनुमान लगा सकते हैं?)

यदि आप Bपहले ऑपरेंड के रूप में कोशिश करते हैं , दोनों B || Aऔर B && Aरनटाइम बाइंडर अपवाद देते हैं ( B | Aऔर B & Aठीक काम करते हैं क्योंकि गैर-शॉर्ट-सर्कुलेटिंग ऑपरेटरों के साथ सब कुछ सामान्य है |और &)।

(दृश्य स्टूडियो 2013 के C # संकलक के साथ प्रयास किया गया, और रनटाइम संस्करण .NET 4.5.2।)


4
इसमें Nullable<Boolean>बिल्कुल भी शामिल नहीं हैं , केवल बॉक्स वाले बूलियन के रूप में इलाज किया जाता है dynamic- आपका परीक्षण bool?अप्रासंगिक है। (बेशक, यह एक पूर्ण उत्तर नहीं है, केवल एक कीटाणु।)
जेरोइन मोस्टर्ट

3
A || B, भावना की एक निश्चित राशि में आता है में है कि आप नहीं का मूल्यांकन करना चाहते Bजब तक कि Aगलत है, जो यह नहीं है। इसलिए आप कभी भी अभिव्यक्ति के प्रकार को नहीं जानते हैं। A && Bसंस्करण अधिक आश्चर्य की बात है - मैं मैं कल्पना में क्या पा सकते हैं देखेंगे।
जॉन स्कीट

2
@JeroenMostert: ठीक है, जब तक कि संकलक तय करने के लिए कि अगर के प्रकार के थे Aहै boolऔर का मान Bहै null, तो एक bool && bool?ऑपरेटर शामिल किया जा सकता है।
जॉन स्कीट

4
दिलचस्प है, ऐसा लग रहा है कि इसने एक संकलक या कल्पना बग को उजागर किया है। सी # 5.0 विनिर्देशन &&इसे हल करने के बारे में बात करता है जैसे कि यह &इसके बजाय थे , और विशेष रूप से इसमें वह मामला भी शामिल है जहां दोनों ऑपरेंड हैं bool?- लेकिन फिर अगला खंड यह संदर्भित करता है कि अशक्त मामले को संभालना नहीं है। मैं उस पर और अधिक विस्तार में जाने वाले उत्तर को जोड़ सकता हूं, लेकिन यह पूरी तरह से नहीं समझाएगा।
जॉन स्कीट

14
मैंने मद समस्या के बारे में मैड्स को ईमेल किया है, यह देखने के लिए कि क्या मैं इसे पढ़ रहा हूं, यह सिर्फ एक मुद्दा है ...
जॉन स्कीट

जवाबों:


67

सबसे पहले, यह इंगित करने के लिए धन्यवाद कि गैर-गतिशील शून्य-बूल मामले पर कल्पना स्पष्ट नहीं है। मैं भविष्य के संस्करण में इसे ठीक कर दूंगा। संकलक का व्यवहार अभिप्रेत व्यवहार है; &&और ||अशक्त बूलों पर काम करने वाले नहीं हैं।

गतिशील बांधने की मशीन इस प्रतिबंध को लागू करने के लिए नहीं लगती है, हालांकि। : इसके बजाय, यह घटक को अलग से संचालन बांधता है &/ |और ?:। इस प्रकार अगर यह पहला ऑपरेंड होता है trueया false(जो बूलियन मान हैं और इस तरह पहले ऑपरेंड के रूप में अनुमति दी जाती है ?:) तो आप इससे छेड़छाड़ करने में सक्षम हैं , लेकिन यदि आप nullपहले ऑपरेंड के रूप में देते हैं (जैसे कि यदि आप B && Aऊपर दिए गए उदाहरण में कोशिश करते हैं), तो आप करते हैं एक रनटाइम बाइंडिंग अपवाद मिलता है।

यदि आप इसके बारे में सोचते हैं, तो आप देख सकते हैं कि हमने एक बड़े डायनेमिक ऑपरेशन के बजाय डायनेमिक &&और ||इस तरह से क्यों लागू किया: डायनेमिक ऑपरेशन उनके संचालन के मूल्यांकन के बाद रनटाइम पर बाध्य होते हैं , ताकि बाइंडिंग रनटाइम के परिणामों के आधार पर हो सके उन मूल्यांकन के। लेकिन इस तरह के उत्सुक मूल्यांकन शॉर्ट-सर्कुलेटिंग ऑपरेटरों के उद्देश्य को हरा देते हैं! इसलिए इसके बजाय, गतिशील के लिए उत्पन्न कोड &&और ||मूल्यांकन को टुकड़ों में तोड़ता है और निम्नानुसार आगे बढ़ेगा:

  • बाएं ऑपरेंड का मूल्यांकन करें (परिणाम को कॉल करें x)
  • इसे boolअंतर्निहित रूपांतरण, trueया falseपरिचालकों के माध्यम से चालू करने का प्रयास करें (यदि असमर्थ हों तो विफल)
  • xएक ?:ऑपरेशन में स्थिति के रूप में उपयोग करें
  • सच्ची शाखा में, xपरिणाम के रूप में उपयोग करें
  • झूठी शाखा में, अब दूसरे ऑपरेंड का मूल्यांकन करें (परिणाम को कॉल करें y)
  • रनटाइम प्रकार के आधार पर &या |ऑपरेटर को बांधने की कोशिश करें xऔर y(विफल होने पर विफल)
  • चयनित ऑपरेटर को लागू करें

यह वह व्यवहार है जो ऑपरेंड्स के कुछ "अवैध" संयोजनों के माध्यम से देता है: ?:ऑपरेटर सफलतापूर्वक पहले ऑपरेटर को एक गैर-अशक्त बूलियन के रूप में मानता है , &या |ऑपरेटर सफलतापूर्वक इसे एक अशक्त बूलियन के रूप में मानता है , और दोनों यह जांचने के लिए कभी भी सहमत नहीं होते हैं कि वे सहमत हैं ।

तो ऐसा नहीं है कि गतिशील && और || nullables पर काम करते हैं। यह सिर्फ इतना है कि वे इस तरह से लागू किए जाते हैं जो कि स्थिर मामले की तुलना में थोड़ा बहुत हल्का है। इसे शायद एक बग माना जाना चाहिए, लेकिन हम इसे कभी ठीक नहीं करेंगे, क्योंकि यह एक ब्रेकिंग परिवर्तन होगा। इसके अलावा, यह शायद ही किसी को व्यवहार को कसने में मदद करेगा।

उम्मीद है कि यह बताता है कि क्या होता है और क्यों! यह एक पेचीदा क्षेत्र है, और मैं अक्सर अपने आप को निर्णयों के परिणामों से चकित पाता हूं, जब हमने गतिशील लागू किया था। यह सवाल स्वादिष्ट था - इसे ऊपर लाने के लिए धन्यवाद!

मैड्स


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

6

क्या यह सही व्यवहार है?

हाँ, मुझे पूरा यकीन है कि यह है।

आप कल्पना से कैसे अनुमान लगा सकते हैं?

सी # विनिर्देश संस्करण 5.0 की धारा 7.12 में सशर्त ऑपरेटरों के बारे में जानकारी है &&और|| और कैसे गतिशील बंधन उन से संबंधित है। प्रासंगिक अनुभाग:

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

यह महत्वपूर्ण बिंदु है जो आपके प्रश्न का उत्तर देता है, मुझे लगता है। रन-टाइम पर क्या होता है संकल्प? खंड 7.12.2, उपयोगकर्ता-परिभाषित सशर्त तार्किक ऑपरेटर बताते हैं:

  • ऑपरेशन x && y का मूल्यांकन T.false (x) के रूप में किया जाता है? x: T। & (x, y), जहां T.false (x) T में घोषित ऑपरेटर का एक आह्वान है, और T। & T। (x, y) चयनित ऑपरेटर का एक आह्वान है &
  • ऑपरेशन x || y का मूल्यांकन T.true (x) के रूप में किया जाता है? x: T. (x, y), जहां T.true (x) T में घोषित किए गए ऑपरेटर के सही होने का एक आह्वान है, और T. | (x, y) चयनित ऑपरेटर का एक आह्वान है।

दोनों ही मामलों में, पहले ऑपरेंड x को falseया trueऑपरेटर्स का उपयोग करके एक बूल में परिवर्तित किया जाएगा । तब उपयुक्त तार्किक ऑपरेटर कहा जाता है। इसे ध्यान में रखते हुए, हमारे पास आपके बाकी सवालों के जवाब देने के लिए पर्याप्त जानकारी है।

लेकिन ए के xx के लिए मूल्यांकन || B कोई बाध्यकारी-समय अपवाद नहीं है, और केवल संपत्ति A को पढ़ा गया था, B नहीं। ऐसा क्यों होता है?

के लिए ||ऑपरेटर, हम इसे इस प्रकार जानते हैं true(A) ? A : |(A, B)। हम शॉर्ट सर्किट करते हैं, इसलिए हमें एक बाध्यकारी समय अपवाद नहीं मिलेगा। यहां तक कि अगर Aथा false, हम हैं अभी भी नहीं एक क्रम बाध्यकारी अपवाद है, क्योंकि निर्दिष्ट संकल्प कदम के मिलता है। यदि Aहै false, तो हम |ऑपरेटर करते हैं, जो कि धारा 7.11.4 के अनुसार, शून्य मानों को सफलतापूर्वक संभाल सकता है।

A && B (yy के लिए) का मूल्यांकन करने से कोई बाध्यकारी समय त्रुटि भी होती है। और यहाँ दोनों गुणों को पुनः प्राप्त किया जाता है, निश्चित रूप से। रन-टाइम बाइंडर द्वारा इसकी अनुमति क्यों दी गई है? यदि B से दी गई ऑब्जेक्ट को "खराब" ऑब्जेक्ट (स्ट्रिंग की तरह) में बदल दिया जाए, तो एक बाइंडिंग अपवाद उत्पन्न होता है।

इसी तरह के कारणों के लिए, यह भी काम करता है। &&के रूप में मूल्यांकन किया है false(x) ? x : &(x, y)Aसफलतापूर्वक एक में परिवर्तित किया जा सकता है bool, इसलिए वहाँ कोई मुद्दा नहीं है। क्योंकि Bअशक्त है, &ऑपरेटर को उठा लिया जाता है (धारा 7.3.7) उस से जो कि लेता है एक boolको लेता हैbool? पैरामीटर , और इस प्रकार कोई रनटाइम अपवाद नहीं है।

दोनों सशर्त संचालकों के लिए, अगर Bबूल (या अशक्त गतिशील) के अलावा कुछ भी हो , तो रनटाइम बाइंडिंग विफल हो जाती है क्योंकि यह एक अधिभार नहीं पा सकता है जो मापदंडों के रूप में एक बूल और एक गैर-बूल लेता है। हालाँकि, यह केवल तब होता है जब Aऑपरेटर के लिए पहली सशर्त को संतुष्ट करने में विफल रहता है (true के लिए ||, falseके लिए &&)। ऐसा होने का कारण यह है कि गतिशील बंधन काफी आलसी है। यह तार्किक ऑपरेटर को बांधने की कोशिश नहीं करेगा जब तक कि Aयह गलत न हो और इसे तार्किक ऑपरेटर का मूल्यांकन करने के लिए उस रास्ते पर जाना होगा। एक बार Aऑपरेटर के लिए पहली शर्त को पूरा करने में विफल रहता है, यह बाध्यकारी अपवाद के साथ विफल हो जाएगा।

यदि आप B को पहले ऑपरेंड के रूप में आज़माते हैं, दोनों B || A और B && एक रनटाइम बाइंडर अपवाद देते हैं।

उम्मीद है, अब तक, आप पहले से ही जानते हैं कि ऐसा क्यों होता है (या मैंने समझाते हुए एक बुरा काम किया)। इस सशर्त ऑपरेटर को हल करने में पहला कदम पहला ऑपरेंड लेना है B, और तार्किक ऑपरेशन को संभालने से पहले बूल रूपांतरण ऑपरेटरों ( false(B)या true(B)) में से एक का उपयोग करना है। बेशक B, होने के नाते या nullतो परिवर्तित नहीं किया जा सकता है , और इसलिए रनटाइम बाइंडिंग अपवाद होता है।truefalse


कोई आश्चर्य नहीं कि dynamicबाध्यकारी वास्तविक समय के उदाहरणों का उपयोग करके रन-टाइम पर होता है, न कि संकलन-समय के प्रकारों (आपका पहला उद्धरण) पर। आपका दूसरा उद्धरण अप्रासंगिक है क्योंकि यहां कोई भी प्रकार ओवरलोड नहीं करता है operator trueऔर operator false। एकexplicit operator लौटने boolसे कुछ और है operator trueऔर false। किसी भी तरह से युक्ति को पढ़ना मुश्किल है जो A && B(मेरे उदाहरण में) अनुमति देता है, बिना यह भी अनुमति देता है a && bकि कहां-कहां aऔर संकलित समय पर बाध्यकारी के साथ bसांख्यिकीय रूप से अशक्त बूलियन हैं, bool? aऔर bool? b। फिर भी वह अस्वीकृत है।
जेपी स्टिग नीलसन

-1

अशक्त प्रकार सशर्त तार्किक संचालकों को परिभाषित नहीं करता है || तथा &&। मैं आपको निम्नलिखित कोड सुझाता हूं:

bool a = true;
bool? b = null;

bool? xxxxOR = (b.HasValue == true) ? (b.Value || a) : a;
bool? xxxxAND = (b.HasValue == true) ? (b.Value && a) : false;
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.