केस ब्लॉक्स में C # की स्थानीय गुंजाइश क्यों नहीं है?


38

मैं यह कोड लिख रहा था:

private static Expression<Func<Binding, bool>> ToExpression(BindingCriterion criterion)
{
    switch (criterion.ChangeAction)
    {
        case BindingType.Inherited:
            var action = (byte)ChangeAction.Inherit;
            return (x => x.Action == action);
        case BindingType.ExplicitValue:
            var action = (byte)ChangeAction.SetValue;
            return (x => x.Action == action);
        default:
            // TODO: Localize errors
            throw new InvalidOperationException("Invalid criterion.");
    }
}

और एक संकलन त्रुटि खोजने के लिए आश्चर्यचकित था:

'स्क्रिया' नामक एक स्थानीय चर पहले से ही इस दायरे में परिभाषित किया गया है

यह हल करने के लिए एक बहुत आसान मुद्दा था; बस दूसरे से छुटकारा पाने की varकोशिश की।

caseब्लॉकों में घोषित रूप से चर में माता-पिता का दायरा होता है switch, लेकिन मैं उत्सुक हूं कि ऐसा क्यों है। यह देखते हुए कि सी # निष्पादन अन्य मामलों के माध्यम से गिरावट (यह अनुमति नहीं है की आवश्यकता है break , return, throw, या goto caseहर के अंत में बयान caseब्लॉक), यह काफी अजीब लगता है कि यह चर घोषणाओं की अनुमति होगी के अंदर एक caseकिसी अन्य में चर के साथ इस्तेमाल किया या संघर्ष किए जाने की case। दूसरे शब्दों में, चर caseबयानों के माध्यम से गिरते दिखाई देते हैं , हालांकि निष्पादन नहीं हो सकता है। सी # अन्य भाषाओं के कुछ निर्माणों को भ्रमित करने या आसानी से दुरुपयोग करने पर रोक लगाकर पठनीयता को बढ़ावा देने के लिए बहुत पीड़ा देता है। लेकिन ऐसा लगता है कि यह सिर्फ भ्रम पैदा करने के लिए बाध्य है। निम्नलिखित परिदृश्यों पर विचार करें:

  1. अगर इसे इसे बदलना था:

    case BindingType.Inherited:
        var action = (byte)ChangeAction.Inherit;
        return (x => x.Action == action);
    case BindingType.ExplicitValue:
        return (x => x.Action == action);
    

    मुझे " अप्रमाणित स्थानीय चर 'कार्रवाई' का उपयोग मिलता है "। यह भ्रामक है क्योंकि C # में प्रत्येक अन्य निर्माण में मैं var action = ...चर को आरंभ करने के बारे में सोच सकता हूं , लेकिन यहां यह केवल इसे घोषित करता है।

  2. अगर मुझे इस तरह के मामलों की अदला-बदली करनी थी:

    case BindingType.ExplicitValue:
        action = (byte)ChangeAction.SetValue;
        return (x => x.Action == action);
    case BindingType.Inherited:
        var action = (byte)ChangeAction.Inherit;
        return (x => x.Action == action);
    

    घोषित होने से पहले मुझे " स्थानीय चर 'क्रिया का उपयोग नहीं किया जा सकता है "। इसलिए केस ब्लॉक का क्रम यहां एक तरह से महत्वपूर्ण प्रतीत होता है जो पूरी तरह से स्पष्ट नहीं है - आम तौर पर मैं इन्हें किसी भी क्रम में लिख सकता हूं जो मैं चाहता हूं, लेकिन क्योंकि varपहले ब्लॉक actionका उपयोग किया जाना चाहिए , जहां मुझे caseब्लॉक ब्लॉक करना होगा तदनुसार।

  3. अगर इसे इसे बदलना था:

    case BindingType.Inherited:
        var action = (byte)ChangeAction.Inherit;
        return (x => x.Action == action);
    case BindingType.ExplicitValue:
        action = (byte)ChangeAction.SetValue;
        goto case BindingType.Inherited;
    

    तब मुझे कोई त्रुटि नहीं मिलती है, लेकिन एक अर्थ में, ऐसा लगता है कि घोषित होने से पहले चर को एक मान सौंपा जा रहा है।
    (हालांकि मैं किसी भी समय के बारे में नहीं सोच सकता जो आप वास्तव में ऐसा करना चाहते हैं - मुझे goto caseआज से पहले पता भी नहीं था)

तो मेरा सवाल यह है कि, C # के डिज़ाइनरों ने caseअपने स्थानीय स्कोप को ब्लॉक क्यों नहीं किया ? क्या इसके कोई ऐतिहासिक या तकनीकी कारण हैं?


4
बयान actionसे पहले अपने चर को घोषित करें switch, या प्रत्येक मामले को अपने ब्रेसिज़ में डालें, और आपको समझदार व्यवहार मिलेगा।
रॉबर्ट हार्वे

3
@RobertHarvey हाँ, और मैं आगे भी जा सकता था और बिना किसी का उपयोग किए इसे लिख सकता था switch- मैं इस डिज़ाइन के पीछे तर्क के बारे में उत्सुक हूं।
pswg

अगर c # को हर मामले के बाद विराम की आवश्यकता है तो मुझे लगता है कि यह ऐतिहासिक कारणों से होना चाहिए जैसा कि c / java में नहीं है!
tgkprog

@tgkprog मेरा मानना ​​है कि यह ऐतिहासिक कारण नहीं है और यह कि C # के डिजाइनरों ने यह सुनिश्चित करने के उद्देश्य से कि C # में भूलने की सामान्य गलती breakसंभव नहीं है।
svick

12
इस संबंधित SO प्रश्न पर एरिक लिपर्ट का उत्तर देखें। stackoverflow.com/a/1076642/414076 आप संतुष्ट हो सकते हैं या नहीं भी आ सकते हैं। यह पढ़ता है "क्योंकि यही उन्होंने 1999 में इसे करने के लिए चुना था।"
एंथनी पेग्राम

जवाबों:


24

मुझे लगता है कि एक अच्छा कारण यह है कि हर दूसरे मामले में, एक "सामान्य" स्थानीय चर का दायरा ब्रेसिज़ ( ) द्वारा सीमांकित एक ब्लॉक है {}। स्थानीय चर जो सामान्य नहीं होते हैं एक कथन (जो आमतौर पर एक ब्लॉक है) से पहले एक विशेष निर्माण में दिखाई देते हैं, जैसे कि forलूप वेरिएबल या में एक चर using

LINQ क्वेरी अभिव्यक्तियों में एक और अपवाद स्थानीय चर हैं, लेकिन वे सामान्य स्थानीय चर घोषणाओं से पूरी तरह से अलग हैं, इसलिए मुझे नहीं लगता कि वहां भ्रम की संभावना है।

संदर्भ के लिए, नियम C # युक्ति के .73.7 दायरे में हैं:

  • स्थानीय-चर-घोषणा में घोषित स्थानीय चर का दायरा वह ब्लॉक होता है जिसमें घोषणा होती है।

  • एक बयान के स्विच-ब्लॉक में घोषित स्थानीय चर का दायरा स्विच-ब्लॉक हैswitch

  • एक स्थानीय चर का दायरा घोषित में एक के लिए-प्रारंभकर्ता एक के forबयान है के लिए-प्रारंभकर्ता , के लिए शर्त , के लिए-इटरेटर , और निहित बयान के forबयान।

  • दिए गए निर्माण के विस्तार से एक चर के दायरे को फ़ार्च-स्टेटमेंट , उपयोग-स्टेटमेंट , लॉक-स्टेटमेंट या क्वेरी-एक्सप्रेशन के हिस्से के रूप में घोषित किया जाता है ।

(हालांकि मुझे पूरी तरह से यकीन नहीं है कि switchब्लॉक का उल्लेख क्यों किया गया है, क्योंकि इसमें स्थानीय चर घोषणाओं के लिए कोई विशेष वाक्यविन्यास नहीं है, अन्य सभी उल्लिखित निर्माणों के विपरीत।)


1
+1 क्या आप उस गुंजाइश का लिंक प्रदान कर सकते हैं जिसे आप उद्धृत कर रहे हैं?
pswg

@pswg आप MSDN पर विनिर्देश डाउनलोड करने के लिए एक लिंक पा सकते हैं । (उस पेज पर "Microsoft Developer Network (MSDN)" कहने वाले लिंक पर क्लिक करें।)
svick

इसलिए मैंने जावा स्पेक्स को देखा, और switchesइस संबंध में पहचान के अनुसार व्यवहार करना प्रतीत होता है (सिवाय case's के अंत में जंप की आवश्यकता के अलावा )। ऐसा लगता है कि यह व्यवहार बस वहां से कॉपी किया गया था। इसलिए मुझे लगता है कि संक्षिप्त उत्तर है - caseबयान ब्लॉक नहीं बनाते हैं, वे बस एक switchखंड के विभाजन को परिभाषित करते हैं - और इसलिए उनके पास खुद से स्कोप नहीं हैं।
pswg

3
पुन :: मुझे पूरी तरह से यकीन नहीं है कि स्विच ब्लॉक को स्पष्ट रूप से क्यों उल्लेख किया गया है - कल्पना के लेखक को सिर्फ अचार किया जा रहा है, यह स्पष्ट रूप से इंगित करता है कि स्विच-ब्लॉक में एक नियमित ब्लॉक की तुलना में एक अलग व्याकरण है।
एरिक लिपर्ट

42

लेकिन यह करता है। आप लाइनों को लपेटकर कहीं भी स्थानीय स्कोप बना सकते हैं{}

switch (criterion.ChangeAction)
{
  case BindingType.Inherited:
    {
      var action = (byte)ChangeAction.Inherit;
      return (x => x.Action == action);
    }
  case BindingType.ExplicitValue:
    {
      var action = (byte)ChangeAction.SetValue;
      return (x => x.Action == action);
    }
  default:
    // TODO: Localize errors
    throw new InvalidOperationException("Invalid criterion.");
}


1
+1 यह एक अच्छी चाल है, लेकिन मैं अपने मूल प्रश्न को संबोधित करने के लिए आने के लिए svick के उत्तर को स्वीकार करने जा रहा हूं ।
pswg

10

मैं एरिक लिपर्ट को उद्धृत करूंगा, जिसका उत्तर इस विषय पर बहुत स्पष्ट है:

एक उचित सवाल यह है कि "यह कानूनी क्यों नहीं है?" एक उचित जवाब है "ठीक है, यह क्यों होना चाहिए"? आपके पास इसके दो तरीके हो सकते हैं। या तो यह कानूनी है:

switch(y) 
{ 
    case 1:  int x = 123; ...  break; 
    case 2:  int x = 456; ...  break; 
}

या यह कानूनी है:

switch(y) 
{
    case 1:  int x = 123; ... break; 
    case 2:  x = 456; ... break; 
}

लेकिन आपके पास इसके दोनों तरीके नहीं हो सकते। C # के डिजाइनरों ने ऐसा करने के लिए अधिक प्राकृतिक तरीका प्रतीत होने के रूप में दूसरा तरीका चुना।

यह निर्णय 7 जुलाई, 1999 को किया गया था, जो दस साल पहले शर्मसार हुआ था। उस दिन से नोटों में टिप्पणियां बेहद संक्षिप्त हैं, बस "एक स्विच-केस अपनी घोषणा स्थान नहीं बनाता है" और फिर कुछ नमूना कोड देते हैं जो दिखाता है कि क्या काम करता है और क्या नहीं करता है।

इस विशेष दिन के बारे में डिजाइनरों के मन में क्या था, इसके बारे में और जानने के लिए, मुझे बहुत से लोगों को इस बारे में बताना होगा कि वे दस साल पहले क्या सोच रहे थे - और आखिरकार क्या एक तुच्छ मुद्दा है; मैं ऐसा नहीं करने जा रहा हूं।

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

इसलिए, जब तक कि आप एरिक लिपर्ट की तुलना में 1999 की C # डेवलपर टीम के साथ अधिक इंटिमेट नहीं होंगे, तब तक आपको इसका सही कारण कभी पता नहीं चलेगा!


एक नीच नहीं, लेकिन यह कल के सवाल पर की गई टिप्पणी थी
जेसी सी। स्लीकर

4
मुझे वह दिखाई देता है, लेकिन चूंकि टिप्पणीकार एक उत्तर नहीं देता है, इसलिए मुझे लगा कि लिंक की सामग्री को स्पष्ट रूप से बनाने के लिए एक अच्छा विचार हो सकता है। और मैं downvotes परवाह नहीं है! ओपी ऐतिहासिक कारण पूछता है, "यह कैसे करना है" जवाब नहीं।
सिरिल गैंडन

5

स्पष्टीकरण सरल है - ऐसा इसलिए है क्योंकि यह सी में है। सी + +, जावा और सी # जैसी भाषाओं ने परिचित के लिए स्विच स्टेटमेंट के सिंटैक्स और स्कूपिंग की नकल की है।

(जैसा कि एक अन्य उत्तर में कहा गया है, C # के डेवलपर्स के पास इस बात का दस्तावेजीकरण नहीं है कि केस-स्कोप के बारे में यह निर्णय कैसे किया गया था। लेकिन C # सिंटैक्स के लिए अस्थिर सिद्धांत यह था कि जब तक उनके पास इसे अलग-अलग करने के लिए मजबूर करने के कारण नहीं थे, वे जावा को कॉपी करते हैं। )

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

(अधिक मौलिक रूप से। गोटो की संरचना नहीं है - वे कोड के परिभाषित या सीमांकित खंड नहीं करते हैं , वे केवल जंप-पॉइंट्स को परिभाषित करते हैं। इसलिए एक गोटो लेबल एक गुंजाइश पेश नहीं कर सकता है ।)

C # सिंटैक्स को बरकरार रखता है, लेकिन प्रत्येक (गैर-खाली) केस क्लॉज के बाद बाहर निकलने की आवश्यकता के द्वारा "गिरना" से बचाव करता है। लेकिन यह बदलता है कि हम स्विच के बारे में कैसे सोचते हैं! मामले अब वैकल्पिक शाखाएं हैं , जैसे कि अगर-और में शाखाएं। इसका मतलब है कि हम उम्मीद करेंगे कि प्रत्येक शाखा अपने स्वयं के दायरे को उसी तरह परिभाषित करे, जैसे कि क्लॉस या पुनरावृति क्लॉस।

इसलिए संक्षेप में: मामले समान दायरे को साझा करते हैं क्योंकि यह सी में है। लेकिन यह सी # में अजीब और असंगत लगता है क्योंकि हम मामलों को गोटो के लक्ष्यों के बजाय वैकल्पिक शाखाओं के रूप में सोचते हैं।


1
" यह C # में अजीब और असंगत लगता है क्योंकि हम मामलों को गोटो के लक्ष्यों के बजाय वैकल्पिक शाखाओं के रूप में सोचते हैं " ठीक यही भ्रम था। +1 के पीछे सौंदर्य कारणों को समझाने के लिए कि यह C # में गलत क्यों लगता है।
pswg

4

स्कोप को देखने का एक सरलीकृत तरीका ब्लॉक द्वारा गुंजाइश पर विचार करना है {}

चूंकि switchइसमें कोई ब्लॉक नहीं है, इसलिए इसमें अलग-अलग स्कोप नहीं हो सकते।


3
किस बारे में for (int i = 0; i < n; i++) Write(i); /* legal */ Write(i); /* illegal */? कोई ब्लॉक नहीं हैं, लेकिन अलग-अलग स्कोप हैं।
23

@svick: जैसा कि मैंने कहा, सरलीकृत, एक ब्लॉक है, जिसे forस्टेटमेंट द्वारा बनाया गया है । switchप्रत्येक मामले के लिए ब्लॉक नहीं बनाता है, बस शीर्ष स्तर पर। समानता यह है कि प्रत्येक कथन एक ब्लॉक बनाता है ( switchकथन के रूप में गिना जाता है)।
ग्वेंटे

1
फिर आप caseइसके बजाय क्यों नहीं गिन सकते थे ?
svick

1
@ शविक: क्योंकि caseइसमें एक ब्लॉक नहीं है, जब तक कि आप वैकल्पिक रूप से एक जोड़ने का फैसला नहीं करते हैं। forअगले बयान के लिए रहता है जब तक आप जोड़ते हैं {}तो यह हमेशा एक ब्लॉक होता है, लेकिन caseतब तक रहता है जब तक कि कुछ आपको वास्तविक ब्लॉक, switchबयान को छोड़ने का कारण नहीं बनता है ।
ग्वेंटे
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.