लूप के लिए घोषित चर स्थानीय चर है?


133

मैं काफी समय से C # का उपयोग कर रहा हूं लेकिन कभी भी निम्नलिखित का एहसास नहीं हुआ:

 public static void Main()
 {
     for (int i = 0; i < 5; i++)
     {

     }

     int i = 4;  //cannot declare as 'i' is declared in child scope                
     int A = i;  //cannot assign as 'i' does not exist in this context
 }

तो मैं ब्लॉक के लिए 'i' के मूल्य का उपयोग क्यों नहीं कर सकता यदि यह मुझे इस नाम के साथ एक चर घोषित करने की अनुमति नहीं देता है?

मैंने सोचा था कि फॉर-लूप द्वारा उपयोग किया जाने वाला इटेरिएटर वैरिएबल केवल इसके दायरे में मान्य है।


8
क्योंकि बाहरी ब्लॉक स्कोप में लूप के लिए गुंजाइश शामिल है
V4Vendetta

3
एक तरह से मैं इसके बारे में सोचता हूं (और कुछ कोड शैली गाइडों को इसकी आवश्यकता होती है, विशेष रूप से गतिशील-टाइप भाषाओं के लिए) यह है कि एक दायरे में घोषित सभी चर उस दायरे की शुरुआत में घोषित किए जा सकते हैं, जिसका अर्थ है कि आपका कार्य फिर से लिखा जा सकता हैint i, A; for(int i = 0; i < 5; i++){ } i=4; A=i
कीथ

2
@ V4Vendetta: यह आसपास का दूसरा तरीका है। आंतरिक ब्लॉक पैरेंट ब्लॉक के लिए एक ब्लैक बॉक्स है।
सेबेस्टियन मच

4
तकनीकी कारणों के अलावा यह क्यों संभव नहीं है, ऐसा क्यों (या इस का एक प्रकार) कभी भी करने के लिए एक समझदार बात होगी ?! यह स्पष्ट रूप से एक अलग उद्देश्य प्रदान करता है, इसलिए इसे एक अलग नाम दें।
डेव

मुझे इसका बिल्कुल एहसास नहीं था, लेकिन यह पूरी तरह से प्रतिबंध की तरह लगता है।
alan2here

जवाबों:


119

जिस कारण से आपको फॉर-लूप और फॉर-लूप दोनों में एक ही नाम के साथ एक वैरिएबल को परिभाषित करने की अनुमति नहीं है, क्योंकि बाहरी-दायरे में वैरिएबल इनर-स्कोप में मान्य हैं। इसका मतलब है कि अगर अनुमति दी गई थी तो लूप के भीतर दो 'i' वैरिएबल होंगे।

देखें: MSDN स्कोप्स

विशेष रूप से:

स्थानीय-चर-घोषणा (धारा 8.5.1) में घोषित स्थानीय चर का दायरा वह खंड है जिसमें घोषणा होती है।

तथा

किसी कथन के लिए-प्रारंभ-प्रारंभकर्ता (धारा is. is.३) में घोषित एक स्थानीय चर का दायरा, प्रारंभिक-प्रारंभक, for-condition, for-iterator, और कथन के लिए निहित कथन है।

और भी: स्थानीय चर घोषणाएं (सी # विनिर्देश की धारा 8.5.1)

विशेष रूप से:

स्थानीय चर-घोषणा में घोषित स्थानीय चर का दायरा वह खंड होता है जिसमें घोषणा होती है। स्थानीय चर को स्थानीय चर के स्थानीय-चर-घोषणाकर्ता से पहले एक स्थानीय चर को संदर्भित करना एक त्रुटि है।स्थानीय चर के दायरे में, एक अन्य स्थानीय चर या एक ही नाम के साथ स्थिर घोषित करने के लिए एक संकलन-समय त्रुटि है।

(जोर मेरा)

जिसका अर्थ है कि iआपके लूप के अंदर का दायरा फॉर-लूप है। जबकि iआपके फॉर-लूप के बाहर का दायरा पूरी मुख्य विधि है और फॉर-लूप है। मतलब आपके पास दो घटनाएं होंगीi लूप अंदर होंगी जो उपरोक्त के अनुसार अमान्य है।

कारण यह है कि आपको करने की अनुमति नहीं int A = i;है क्योंकि लूप के int iभीतर उपयोग के लिए केवल स्कूप किया गया forहै। इस प्रकार यह अब forलूप के बाहर सुलभ नहीं है ।

जैसा कि आप देख सकते हैं कि ये दोनों मुद्दे स्कूपिंग का परिणाम हैं; पहला अंक ( int i = 4;) लूप स्कोप के iभीतर दो चर में होगा for। जबकि int A = i;इसके परिणामस्वरूप एक वैरिएबल का उपयोग होगा जो कि दायरे से बाहर है।

इसके बजाय आप जो कर सकते हैं i, उसे पूरी विधि से करने की घोषणा की जाती है, और फिर दोनों विधि के साथ-साथ लूप स्कोप के लिए भी इसका उपयोग करें। यह या तो नियम तोड़ने से बच जाएगा।

public static void Main()
{
    int i;

    for (i = 0; i < 5; i++)
    {

    }

    // 'i' is only declared in the method scope now, 
    // no longer in the child scope -> valid.
    i = 4;

    // 'i' is declared in the method's scope -> valid. 
    int A = i;
}

संपादित करें :

इस कोड को काफी मान्य रूप से संकलित करने की अनुमति देने के लिए C # संकलक को निश्चित रूप से बदला जा सकता है। आखिर यह मान्य है:

for (int i = 0; i < 5; i++)
{
    Console.WriteLine(i);
}

for (int i = 5; i > 0; i--)
{
    Console.WriteLine(i);
}

लेकिन क्या यह वास्तव में आपके कोड की पठनीयता और रखरखाव के लिए फायदेमंद होगा, जैसे कि कोड लिखने में सक्षम होना:

public static void Main()
{
    int i = 4;

    for (int i = 0; i < 5; i++)
    {
        Console.WriteLine(i);
    }

    for (int i = 5; i > 0; i--)
    {
        Console.WriteLine(i);
    }

    Console.WriteLine(i);
}

यहां गलतियों के बारे में सोचें, क्या आखिरी iप्रिंट 0 या 4 है? अब यह एक बहुत छोटा सा उदाहरण है, जिसे फॉलो करना और ट्रैक करना काफी आसान है, लेकिन यह निश्चित रूप से बहुत कम रख-रखाव और पठनीय है, iजिसे एक अलग नाम से बाहरी घोषित किया गया है ।

ध्यान दें:

कृपया ध्यान दें, C # के स्कोपिंग नियम C ++ के स्कोपिंग नियमों से भिन्न हैं । सी ++ में चर केवल उस दायरे में होते हैं जहां से उन्हें ब्लॉक के अंत तक घोषित किया जाता है। जो आपके कोड को C ++ में वैध निर्माण बना देगा।


समझ में आता है, मुझे लगता है कि यह iहालांकि आंतरिक चर पर त्रुटि चाहिए ; यह मुझे और अधिक स्पष्ट लगता है।
जॉर्ज डकेट

लेकिन स्कोप कमांड और उसके {} के लिए है? या इसका अर्थ इसके माता-पिता {} से है?
जॉन वी

2
यदि मैं बयान के लिए 'ए' घोषित करता हूं, तो यह लूप के लिए वैध नहीं है क्योंकि यह बाद में घोषित किया गया है। यही कारण है कि मुझे नहीं मिलता है कि एक ही नाम का उपयोग क्यों नहीं किया जा सकता है।
जॉन वी

9
शायद यह जवाब, पूर्णता के लिए, यह इंगित करना चाहिए कि इस मामले में C ++ गुंजाइश नियम C ++ के लिए अलग हैं । C ++ में, वैरिएबल केवल उस दायरे में होते हैं, जहां से उन्हें ब्लॉक के अंत तक घोषित किया जाता है (देखें msdn.microsoft.com/en-us/library/b7kfh662(v=vs.80).aspx )।
AAT

2
ध्यान दें कि जावा C ++ और C # के बीच एक मध्यवर्ती दृष्टिकोण लेता है, क्योंकि यह ओपी का उदाहरण जावा में मान्य होगा, लेकिन अगर बाहरी iपरिभाषा को लूप के लिए ले जाया गया था, तो आंतरिक iपरिभाषा को अमान्य के रूप में चिह्नित किया जाएगा।
निकोला मुसत्ती 13

29

J.Kommer के जवाब सही है: संक्षेप में, यह गैर-कानूनी है के लिए एक स्थानीय चर एक में घोषित करने की स्थानीय चर घोषणा अंतरिक्ष कि overlaps एक और स्थानीय चर घोषणा अंतरिक्ष एक ही नाम के एक स्थानीय है।

सी # का एक अतिरिक्त नियम है जिसका उल्लंघन यहां भी किया जाता है। अतिरिक्त नियम यह है कि दो अलग-अलग अतिव्यापी स्थानीय चर घोषणा स्थानों के अंदर दो अलग-अलग संस्थाओं को संदर्भित करने के लिए एक सरल नाम का उपयोग करना अवैध है। तो न केवल आपका उदाहरण अवैध है, यह अवैध भी है:

class C
{
    int x;
    void M()
    {
        int y = x;
        if(whatever)
        {
            int x = 123;

क्योंकि अब साधारण नाम "x" को दो अलग-अलग चीजों - "this.x" और स्थानीय "x" का अर्थ करने के लिए "y" के स्थानीय चर घोषणा स्थान के अंदर उपयोग किया जाता है।

इन मुद्दों के अधिक विश्लेषण के लिए http://blogs.msdn.com/b/ericlippert/archive/tags/simple+names/ देखें ।


2
इसके अलावा, यह ध्यान रखना दिलचस्प है कि जब आप परिवर्तन करेंगे तो आपका उदाहरण कोड संकलित होगाint y = this.x;
फिल

4
@ फिल: सही। this.xएक साधारण नाम नहीं है ।
एरिक लिपर्ट

13

iलूप के बाद विधि के अंदर घोषित करने और उपयोग करने का एक तरीका है :

static void Main()
{
    for (int i = 0; i < 5; i++)
    {

    }

    {
        int i = 4;
        int A = i;
    }
}

आप इसे जावा में कर सकते हैं (यह C से उत्पन्न हो सकता है मुझे यकीन नहीं है)। यह एक चर नाम की खातिर थोड़ा गड़बड़ है।


हाँ यह C / C ++ से उत्पन्न होता है।
ब्रांको दिमित्रिजेविक

1
मुझे यह पहले नहीं पता था। नीट भाषा की सुविधा!

@MathiasLykkegaardLorenzen हाँ
क्रिस एस

7

यदि आप अपने लूप i से पहले घोषित कर चुके हैंfor , तो क्या आपको लगता है कि इसे लूप के अंदर घोषित करने के लिए वैध होना चाहिए?

नहीं, क्योंकि तब दोनों का दायरा ओवरलैप हो जाएगा।

के रूप में करने में सक्षम नहीं होने के लिए int A=i;, अच्छी तरह से है कि iकेवल forलूप में मौजूद है, क्योंकि यह करना चाहिए।


7

जे.केमर के जवाब (+1 btw) के अलावा। NET स्कोप के लिए मानक में यह है:

ब्लॉक यदि आप किसी ब्लॉक कंस्ट्रक्शन के भीतर वैरिएबल घोषित करते हैं जैसे कि इफ स्टेटमेंट, तो वैरिएबल का स्कोप ब्लॉक के अंत तक ही होता है। प्रक्रिया समाप्त होने तक जीवनकाल।

प्रक्रिया यदि आप किसी प्रक्रिया के भीतर एक चर घोषित करते हैं, लेकिन किसी भी विवरण के बाहर, गुंजाइश तब तक है जब तक कि अंतिम उप या अंत फ़ंक्शन नहीं है। चर का जीवनकाल प्रक्रियाओं के समाप्त होने तक है।

इस प्रकार, लूप हेडर के लिए डीएलडल्ट किए गए int को केवल लूप ब्लॉक के दौरान स्कोप में रखा जाएगा, लेकिन जब तक Main()कोड पूरा नहीं हो जाता तब तक यह आजीवन चलता रहता है ।


5

इसके बारे में सोचने का सबसे आसान तरीका यह है कि I की बाहरी घोषणा को लूप से ऊपर ले जाना है। यह तो स्पष्ट हो जाना चाहिए।

यह किसी भी तरह से एक ही गुंजाइश है, इसलिए नहीं किया जा सकता है।


4

साथ ही C # के नियम कई बार सख्ती से प्रोग्राम करने के संदर्भ में आवश्यक नहीं हैं, लेकिन क्या आपके कोड को साफ और पठनीय रखने के लिए आवश्यक हैं।

उदाहरण के लिए, वे इसे बना सकते थे ताकि यदि आप इसे लूप के बाद परिभाषित करते हैं तो यह ठीक है, हालांकि यह कोई है जो आपके कोड को पढ़ता है और परिभाषा लाइन याद आती है, तो यह सोच सकता है कि इसे लूप के चर के साथ क्या करना है।


2

Kommer का उत्तर तकनीकी रूप से सही है। मुझे एक ज्वलंत नेत्रहीन स्क्रीन रूपक के साथ इसे स्पष्ट करना चाहिए।

फॉर-ब्लॉक और एनक्लोजिंग आउटर ब्लॉक के बीच एक तरह से ब्लाइंड स्क्रीन होती है जैसे कि फॉर-ब्लॉक के भीतर का कोड बाहरी कोड देख सकता है लेकिन बाहरी ब्लॉक का कोड अंदर का कोड नहीं देख सकता है।

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

तो या तो आप इसे नहीं देखते हैं, या आप C #!


0

इसे उसी तरह से देखें जैसे कि आप किसी ब्लॉक में घोषणा कर सकते हैं :intusing

using (int i = 0) {
  // i is in scope here
}
// here, i is out of scope

हालांकि, चूंकि intयह लागू नहीं होता है , इसलिए ऐसा नहीं IDisposableकिया जा सकता है। यह किसी को यह कल्पना करने में मदद कर सकता है कि एक intचर को निजी दायरे में कैसे रखा जाता है, हालांकि।

कहने का एक और तरीका होगा,

if (true) {
  int i = 0;
  // i is in scope here
}
// here, i is out of scope

आशा है कि यह कल्पना करने में मदद करता है कि क्या चल रहा है।

मैं वास्तव में इस सुविधा को पसंद करता हूं, जैसा intकि forलूप के अंदर से घोषित करने से कोड अच्छा और तंग रहता है।


WTF? यदि आप एक एनईजी टैग करने जा रहे हैं, तो कहने के लिए गेंदें क्यों हैं।
jp2code

नहीं downvoter और मुझे लगता है कि तुलना usingकाफी ठीक है, हालांकि शब्दार्थ अलग हैं (जो शायद इसलिए इसे नीचा दिखाया गया था)। ध्यान दें कि if (true)बेमानी है। इसे हटा दें और आपके पास एक स्कूपिंग ब्लॉक है।
हाबिल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.