2011-01-11 17 views
11

यह वास्तव में this question का एक ऑफशूट है, लेकिन मुझे लगता है कि यह अपने स्वयं के उत्तर के योग्य है।एक उपयोग प्रकार को एक निर्दिष्ट कथन अपरिभाषित व्यवहार के भीतर से संशोधित कर रहा है?

स्थानीय चर एक संसाधन अधिग्रहण में घोषित पढ़ा रहे हैं:

ECMA-334 की धारा 15.13 के अनुसार (using बयान पर, नीचे के रूप में संसाधन अधिग्रहण कहा गया है) केवल, और एक प्रारंभकर्ता शामिल होगा। एक संकलन समय त्रुटि तब होती है, तो एम्बेडेड बयान प्रयास (काम के माध्यम से ++ और -- ऑपरेटर या) इन स्थानीय चर को संशोधित करने या उन्हें ref या out पैरामीटर के रूप में पारित करने के लिए।

ऐसा लगता है कि नीचे दिया गया कोड अवैध क्यों है।

struct Mutable : IDisposable 
{ 
    public int Field; 
    public void SetField(int value) { Field = value; } 
    public void Dispose() { } 
} 

using (var m = new Mutable()) 
{ 
    // This results in a compiler error. 
    m.Field = 10; 
} 

लेकिन इसके बारे में क्या?

using (var e = new Mutable()) 
{ 
    // This is doing exactly the same thing, but it compiles and runs just fine. 
    e.SetField(10); 
} 

ऊपर टुकड़ा अपरिभाषित और/या सी # में अवैध है? यदि यह कानूनी है, तो इस कोड और उपरोक्त spec से उद्धरण के बीच संबंध क्या है? यदि यह अवैध है, तो यह क्यों काम करता है? क्या वहां कुछ सूक्ष्म छेड़छाड़ है जो इसे अनुमति देती है, या यह तथ्य है कि यह केवल भाग्य के लिए जिम्मेदार है (ताकि किसी को इस तरह के हानिकारक दिखने वाले कोड की कार्यक्षमता पर भरोसा नहीं करना चाहिए)?

+0

असाइनमेंट का उपयोग कर एक विधि बुला रहा है? क्या यह '++ 'या' - ऑपरेटरों का उपयोग कर रहा है? क्या यह इसे 'रेफरी' या 'आउट' पैरामीटर के रूप में पास कर रहा है? –

+0

@ आओन: यह मेरा प्रश्न है। एक मान प्रकार पर एक विधि को कॉल करना जो उस मान के राज्य को संशोधित करता है अनिवार्य रूप से असाइनमेंट से अलग नहीं है, है ना? यही कारण है कि एक क्षेत्र को संशोधित करना शायद ही सीमित है? –

+0

आप इस सामान के साथ कहां आते हैं? उल्लेखनीय है कि एक संपत्ति या तो काम नहीं करता है। जो कि एक कारण होने से रिमोटिंग को रोकता है, हुड के नीचे सिर्फ एक विधि कॉल है। वैसे भी लाया। यह बग की तरह quacks। खैर, दोष। एरिक लिपर्ट के नाम का उल्लेख आमतौर पर उन्हें एक यात्रा का भुगतान करने के लिए मिलता है। किया हुआ। –

उत्तर

2

मुझे संदेह है कि यह संकलित और रन है कि SetField(int) एक फ़ंक्शन कॉल है, असाइनमेंट नहीं है या ref या out पैरामीटर कॉल नहीं है। कंपाइलर को (सामान्य रूप से) जानने का कोई तरीका नहीं है कि SetField(int) चर को म्यूटेट करने जा रहा है या नहीं।

यह कल्पना के अनुसार पूरी तरह से कानूनी प्रतीत होता है।

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

दूसरा विकल्प using कथन में घोषित मूल्य प्रकार चर पर किसी भी विधि कॉल की अनुमति नहीं देगा। यह एक बुरा विचार नहीं हो सकता है, क्योंकि एक संरचना पर IDisposable लागू करने के बाद भी वैसे भी परेशानी मांग रही है। लेकिन जब सी # भाषा पहली बार विकसित हुई थी, तो मुझे लगता है कि उन्हें बहुत सारे रोचक तरीके से structs का उपयोग करने की उच्च उम्मीद थी (जैसे GetEnumerator() उदाहरण जिसे आपने मूल रूप से प्रदर्शित किया था)।

+2

यह ध्वनियां उचित और मैं आपसे सहमत होने के इच्छुक हूं। लेकिन सवाल अभी भी बनी हुई है कि क्या यह व्यवहार वास्तव में परिभाषित किया गया है या नहीं। –

+2

मान '++' या '--' या' ref' या 'out' के माध्यम से नहीं बदला जाता है, इसलिए मैं कहूंगा कि इसे अनुमति देने के लिए परिभाषित किया गया है। निचली पंक्ति: उत्परिवर्तनीय structs घोषित मत करो। वे उलझन में हैं। – dtb

+1

@ डीटीबी: मैं आपकी तर्क देख सकता हूं; मुझे इस बात का अनिश्चितता है कि क्या उन विशिष्ट ऑपरेटरों और 'रेफरी' और 'आउट' का उल्लेख spec के शब्द में केवल * उदाहरण * के रूप में शामिल किया गया था कि स्थानीय चर केवल पढ़ने के लिए, या * केवल मामलों * जो अस्वीकृत हैं। किसी भी तरह से यह मेरे लिए थोड़ा अस्पष्ट लगता है। और जब मैं आपसे सामान्य रूप से म्यूटेबल structs का उपयोग नहीं करने के बारे में सहमत हूं, तो वे * बीसीएल में मौजूद हैं ('उपयोग ' नामक मेरे अब-संपादित-आउट उदाहरण पर विचार करें। 'उपयोग' में' एन्यूमेरेटर ')। –

3

मैं इस तरह से कि

using(var m = new Mutable()) 
{ 
    m = new Mutable(); 
} 

मना किया है में मानक पढ़ा होगा - कारण यह है कि obious लगते हैं के साथ। संरचना के लिए क्यों म्यूटेबल की अनुमति नहीं है मुझे धड़कता है। क्योंकि कक्षा के लिए कोड कानूनी है और ठीक से संकलित करता है ... (ऑब्जेक्ट प्रकार मुझे पता है ..)

इसके अलावा मुझे कोई कारण नहीं दिख रहा है कि मूल्य प्रकार की सामग्री को बदलने से आरए खतरे में पड़ता है। किसी को समझाने की परवाह है?

हो सकता है कि किसी को syntx सिर्फ मानक ;-)

पढ़ने में भूलना जाँच मारियो

+2

+1 यह है कि मैंने इसे कैसे पढ़ा। यह नहीं बताता कि वे "अपरिवर्तनीय" हैं, केवल स्थानीय चर ही "केवल पढ़ने के लिए" है। हालांकि, मैं इस बात पर टिप्पणी नहीं कर सकता कि क्यों संरचना संस्करण विफल रहता है। – user7116

+1

कारण यह है कि यह एक मूल्य प्रकार है। एक पाठक मान प्रकार पर एक असाइनमेंट ऑपरेटर को कॉल करने की अनुमति नहीं है। कंपाइलर द्वारा जारी त्रुटि संदेश सिर्फ भ्रामक है। इसका मतलब है कि एक पठनीय संरचना पर आपको किसी सदस्य के आकलन ऑपरेटर को कॉल करने की अनुमति नहीं है। –

+0

मैं 'myStruct.Field = ...' '' myStruct = ... 'के बजाय असाइनमेंट की इस शैली का जिक्र कर रहा था। मैं खो गया हूं क्योंकि आप इस बात के लिए क्यों हैं कि पूर्व को अस्वीकार क्यों किया जाता है। – user7116

2

कर यह क्या मैं ऊपर लिखा सारांश में ऊपर

struct Mutable : IDisposable 
{ 
    public int Field; 
    public void SetField(int value) { Field = value; } 
    public void Dispose() { } 
} 


class Program 

{ 
    protected static readonly Mutable xxx = new Mutable(); 

    static void Main(string[] args) 
    { 
     //not allowed by compiler 
     //xxx.Field = 10; 

     xxx.SetField(10); 

     //prints out 0 !!!! <--- I do think that this is pretty bad 
     System.Console.Out.WriteLine(xxx.Field); 

     using (var m = new Mutable()) 
     { 
      // This results in a compiler error. 
      //m.Field = 10; 
      m.SetField(10); 

      //This prints out 10 !!! 
      System.Console.Out.WriteLine(m.Field); 
     } 



     System.Console.In.ReadLine(); 
    } 
विपरीत

तो, मैं करने के लिए सिफारिश करेंगे एक प्रयोग ब्लॉक के भीतर एक संरचना को संशोधित करने के लिए एक समारोह का उपयोग नहीं करें। ऐसा लगता है कि यह काम करता है, लेकिन भविष्य में काम करना बंद कर सकता है।

मारियो

2

यह व्यवहार अपरिभाषित है। The C# Programming language सी # 4.0 कल्पना खंड 7.6.4 के अंत में में (सदस्य एक्सेस) पीटर सेस्टाफ्ट राज्यों:

दो बुलेटेड अंक कहा "अगर क्षेत्र केवल पढ़ने के लिए है ... तो परिणाम एक मूल्य है" फ़ील्ड में एक स्ट्रक्चर प्रकार है, और उस संरचना प्रकार में एक परिवर्तनीय फ़ील्ड है ( एक अनुशंसित संयोजन नहीं - इस बिंदु पर अन्य टिप्पणियां देखें)।

वह एक उदाहरण प्रदान करता है। मैंने अपना खुद का उदाहरण बनाया जो नीचे अधिक जानकारी प्रदर्शित करता है।

कुछ हद तक अजीब, अगर बजाय रों struct प्रकार एक का उपयोग कर बयान है, जो भी रों अपरिवर्तनीय बनाने का प्रभाव पड़ता है में घोषित की एक स्थानीय चर थे, तो:

फिर, वह पर कहने के लिए चला जाता है s.SetX() अपेक्षित एसएक्स अद्यतन करता है।

यहां हम देखते हैं कि लेखकों में से एक यह स्वीकार करता है कि यह व्यवहार असंगत है। प्रति खंड 7.6.4, केवल पढ़ने वाले फ़ील्ड को मान के रूप में माना जाता है और नहीं बदला जाता है (प्रतियां बदलती हैं)। using बयान में

संसाधन चर एम्बेडेड बयान में केवल पढ़ने के लिए है,

संसाधनों केवल पढ़ने के लिए क्षेत्रों की तरह व्यवहार करना चाहिए: क्योंकि खंड 8.13 का उपयोग कर हमें बताता बयान केवल पढ़ने के रूप में संसाधनों का इलाज। 7.6.4 के नियमों के अनुसार हमें एक मूल्य एक चर के साथ व्यवहार करना चाहिए। लेकिन आश्चर्यजनक रूप से, संसाधन के मूल मूल्य बदल जाती है के रूप में इस उदाहरण में बताया गया:

//Sections relate to C# 4.0 spec 
    class Test 
    { 
     readonly S readonlyS = new S(); 

     static void Main() 
     { 
      Test test = new Test(); 
      test.readonlyS.SetX();//valid we are incrementing the value of a copy of readonlyS. This is per the rules defined in 7.6.4 
      Console.WriteLine(test.readonlyS.x);//outputs 0 because readonlyS is a value not a variable 
      //test.readonlyS.x = 0;//invalid 

      using (S s = new S()) 
      { 
       s.SetX();//valid, changes the original value. 
       Console.WriteLine(s.x);//Surprisingly...outputs 2. Although S is supposed to be a readonly field...the behavior diverges. 
       //s.x = 0;//invalid 
      } 
     } 

    } 

    struct S : IDisposable 
    { 
     public int x; 

     public void SetX() 
     { 
      x = 2; 
     } 

     public void Dispose() 
     { 

     } 
    }  

स्थिति विचित्र है। नीचे की रेखा, केवल पढ़ने योग्य क्षेत्रों को बनाने से बचें।

+0

उपरोक्त। जब एक स्ट्रक्चर वैरिएबल को केवल पढ़ने के लिए "संदर्भ" में माना जाता है, तो उस चर पर उदाहरण विधियों को सीधे नहीं कहा जाता है, उन्हें "सुरक्षा प्रतिलिपि" पर कॉल किया जाता है, जैसा कि मैंने अभी प्रश्न के बारे में टिप्पणी में कहा था। –

+0

मुझे आश्चर्य है कि "बग" जहां 'उपयोग' में चर को पढ़ा नहीं जाता है, केवल इतना तथ्य है कि '(आईडीस्पोजेबल) एस' बॉक्स '। जब हम 'सूची <>' के माध्यम से 'foreach'' करते हैं, तो औपचारिक रूप से हमारे पास 'var (var e = theList.GetEnumerator()) {...}' का उपयोग होता है, जहां 'e' एक संरचना है जिसे * उत्परिवर्तित किया जाना चाहिए 'foreach' प्रगति करने के लिए। [विधि 'MoveNext() '] (http://msdn.microsoft.com/en-us/library/a3207y01.aspx) एक संरचना को बदलता है? जब मेरे पास अधिक समय होगा तो मैं और अधिक शोध करूंगा। –

संबंधित मुद्दे