var / null अजीब व्यवहार के साथ स्विच करें


91

निम्नलिखित कोड दिया गया है:

string someString = null;
switch (someString)
{
    case string s:
        Console.WriteLine("string s");
        break;
    case var o:
        Console.WriteLine("var o");
        break;
    default:
        Console.WriteLine("default");
        break;
}

स्विच स्टेटमेंट पर मेल क्यों हो रहा है case var o?

यह मेरी समझ है जो case string sतब मेल नहीं खाती है s == nullक्योंकि (प्रभावी रूप से) (null as string) != nullझूठ का मूल्यांकन करता है। वी.एस. कोड पर IntelliSense मुझे बताता है कि oयह एक stringसमान है। कोई विचार?


के लिए उपयुक्त: अशक्त जाँच के साथ C # 7 स्विच केस


9
की पुष्टि। मैं इस सवाल का प्यार है, विशेष रूप से अवलोकन है कि के साथ oकिया जाता है string(जेनरिक के साथ पुष्टि की - यानी Foo(o)जहां Foo<T>(T template) => typeof(T).Name) - यह एक बहुत ही दिलचस्प मामला है जहां string xबर्ताव की तुलना में अलग var xभी जब x(संकलक द्वारा) लिखा गया के रूप मेंstring
मार्क Gravell

7
डिफ़ॉल्ट मामला मृत कोड है। विश्वास है कि हमें वहां एक चेतावनी जारी की जानी चाहिए। जाँच हो रही है।
जेयरपावर

13
यह मेरे लिए अजीब है कि C # डिजाइनरों ने varइस संदर्भ में अनुमति देने का निर्णय लिया । यह निश्चित है कि सी ++ में मुझे जिस तरह की चीज़ मिलेगी, ऐसा लगता है कि प्रोग्रामर को "सफलता के गर्त में" ले जाने के लिए निर्दिष्ट भाषा में नहीं। यहां, varअस्पष्ट और बेकार दोनों चीजें हैं, जो कि सी # डिजाइन आमतौर पर बचने का प्रयास करती हैं।
पीटर डनिहो

1
@PeterDuniho मैं बेकार नहीं कहूंगा; switchअनाम अभिव्यक्ति के लिए भीतर का अभिव्यक्ति हो सकता है - गुमनाम प्रकार, आदि; और यह अस्पष्ट नहीं है - संकलक स्पष्ट रूप से प्रकार जानता है; यह सिर्फ भ्रमित करने वाला है (कम से कम मेरे लिए) कि nullनियम इतने भिन्न हैं!
मार्क Gravell

1
@PeterDuniho मज़ेदार तथ्य - हमने एक बार C # 1.2 विनिर्देशन से निश्चित असाइनमेंट के औपचारिक नियमों को देखा था, और ब्लॉक के अंदर (जहाँ यह अब है) इलस्ट्रेटिव एक्सपेंशन कोड में परिवर्तनशील घोषणा थी ; यह केवल 2.0 में बाहर चला गया, फिर वापस अंदर जब कब्जा समस्या स्पष्ट थी।
मार्क ग्रेवेल

जवाबों:


69

एक स्पष्ट प्रकार के लिए एक पैटर्न मिलान switchकथन के अंदर caseपूछ रहा है कि क्या प्रश्न में मूल्य उस विशिष्ट प्रकार का है, या एक व्युत्पन्न प्रकार है। यह बिल्कुल बराबर हैis

switch (someString) {
  case string s:
}
if (someString is string) 

मान के nullपास एक प्रकार नहीं है और इसलिए उपरोक्त शर्तों में से किसी को भी संतुष्ट नहीं करता है। स्थैतिक प्रकार के someStringउदाहरण में खेलने में नहीं आता है।

varप्रकार हालांकि पैटर्न में मिलान को वाइल्ड कार्ड के रूप में कार्य करता है और सहित किसी भी मूल्य से मेल खाएगी null

यहां defaultमामला डेड कोड का है। case var oकिसी भी मूल्य, अशक्त या गैर-शून्य से मेल खाएगी। एक गैर-डिफ़ॉल्ट मामला हमेशा एक डिफ़ॉल्ट पर जीतता है इसलिए defaultकभी भी हिट नहीं होगा। यदि आप IL को देखेंगे तो आप देखेंगे कि यह उत्सर्जित भी नहीं है।

एक नज़र में यह अजीब लग सकता है कि यह बिना किसी चेतावनी के संकलित है (निश्चित रूप से मुझे फेंक दिया)। लेकिन यह C # व्यवहार के साथ मेल खाता है जो 1.0 पर वापस जाता है। कंपाइलर defaultमामलों को तब भी अनुमति देता है जब यह तुच्छ साबित कर सकता है कि यह कभी भी हिट नहीं होगा। एक उदाहरण के रूप में निम्नलिखित पर विचार करें:

bool b = ...;
switch (b) {
  case true: ...
  case false: ...
  default: ...
}

यहां defaultकभी भी हिट नहीं किया जाएगा (यहां तक boolकि इसके लिए एक मूल्य है जो 1 या 0 नहीं है)। फिर भी C # ने चेतावनी के बिना 1.0 से यह अनुमति दी है। पैटर्न मिलान यहाँ इस व्यवहार के अनुरूप है।


4
असली मुद्दा हालांकि यह है कि संकलक "शो" varप्रकार का होने के लिए है stringजब यह वास्तव में नहीं है (ईमानदारी से निश्चित नहीं है कि किस प्रकार को स्वीकार किया जाना चाहिए)
shmuelie

varउदाहरण में @shmuelie के प्रकार की गणना की जाती है string
जरीदपार

5
@JaredPar यहाँ अंतर्दृष्टि के लिए धन्यवाद; व्यक्तिगत रूप से मैं चेतावनियों के अधिक उत्सर्जन का समर्थन करूंगा जब वह पहले ऐसा नहीं करता था, लेकिन मैं भाषा टीम की बाधाओं को समझता हूं। क्या आपने कभी "सब कुछ मोड के बारे में" (संभवतः डिफ़ॉल्ट रूप से), बनाम "विरासत स्टिक मोड" (वैकल्पिक) के बारे में विचार किया है? शायदcsc /stiffUpperLip
मार्क Gravell

3
@MarcGravell में हमारे पास चेतावनी तरंगें नामक एक सुविधा है जो नई चेतावनियों को पेश करने के लिए आसान, कम कम्पीटिव ब्रेकी बनाने के लिए है। अनिवार्य रूप से हर संकलक रिलीज एक नई लहर है और आप चेतावनी / तरंग: 1, / लहर: 2, / लहर के माध्यम से चुन सकते हैं।
जरीदपार

4
@JonathanDickinson मुझे नहीं लगता कि वह दिखाता है जो आप सोचते हैं कि वह दिखाता है। यह दर्शाता है nullकि एक मान्य stringसंदर्भ है, और किसी भी stringसंदर्भ (सहित null) को एक objectसंदर्भ में निहित (संदर्भ-संरक्षण) किया जा सकता objectहै , और किसी भी संदर्भ nullको किसी भी अन्य प्रकार से सफलतापूर्वक (स्पष्ट) किया जा सकता है, फिर भी null। संकलक प्रकार प्रणाली के संदर्भ में वास्तव में एक ही बात नहीं है।
मार्क Gravell

22

मैं एक साथ कई ट्विटर टिप्पणियाँ यहाँ डाल रहा हूँ - यह वास्तव में मेरे लिए नया है, और मैं उम्मीद कर रहा हूँ कि jaredpar अधिक व्यापक उत्तर के साथ कूद जाएगा, लेकिन; लघु संस्करण जैसा कि मैं इसे समझता हूं:

case string s:

के रूप में व्याख्या की है if(someString is string) { s = (string)someString; ...या if((s = (someString as string)) != null) { ... }- जिसमें से एक nullपरीक्षण शामिल है - जो आपके मामले में विफल है; इसके विपरीत:

case var o:

जहां संकलक बस के oरूप stringमें हल करता है o = (string)someString; ...- कोई nullपरीक्षण नहीं , इस तथ्य के बावजूद कि यह सतह पर समान दिखता है, बस संकलक प्रकार प्रदान करता है।

आखिरकार:

default:

यहां नहीं पहुंचा जा सकता है , क्योंकि ऊपर का मामला सब कुछ पकड़ लेता है। यह एक कंपाइलर बग हो सकता है कि यह एक अगम्य कोड चेतावनी का उत्सर्जन नहीं करता है।

मैं मानता हूँ कि यह बहुत ही सूक्ष्म और बारीक है, और भ्रामक है। लेकिन स्पष्ट रूप से case var oपरिदृश्य में अशक्त प्रसार ( o?.Length ?? 0आदि) के साथ उपयोग होता है । मैं मानता हूं कि यह अजीब है कि यह बहुत अलग तरह से var oऔर इसके बीच काम करता है string s, लेकिन यह वही है जो वर्तमान में कंपाइलर करता है।


14

यह इसलिए है क्योंकि गतिशील (रन-टाइम) प्रकार case <Type>पर मेल खाता है , न कि स्टैटिक (संकलन-समय) प्रकार। इसमें डायनेमिक प्रकार नहीं है, इसलिए यह विरुद्ध मिलान नहीं कर सकता है । बस पतन है।nullstringvar

(पोस्टिंग क्योंकि मुझे छोटे जवाब पसंद हैं।)

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