2009-01-07 16 views
220

मैंने इस टिप को एक और प्रश्न में देखा और सोच रहा था कि कोई मुझे बता सकता है कि पृथ्वी पर यह कैसे काम करता है?कोशिश में वास्तव में क्या होता है {वापसी x; } अंत में {x = शून्य; } बयान?

try { return x; } finally { x = null; } 

मेरा मतलब है, finally खंड वास्तव में अमल return बयान के बाद करता है? यह कोड कैसे थ्रेड-असुरक्षित है? क्या आप किसी भी अतिरिक्त हैकरी के बारे में सोच सकते हैं जिसे w.r.t. किया जा सकता है। यह try-finally हैक?

उत्तर

208

नहीं - आईएल स्तर पर आप अपवाद-नियंत्रित ब्लॉक के अंदर से वापस नहीं आ सकते हैं। यह मूलतः एक चर में यह संग्रहीत करता है और

यानी समान करने के लिए बाद में वापस आती है:

int tmp; 
try { 
    tmp = ... 
} finally { 
    ... 
} 
return tmp; 

(उदाहरण के लिए परावर्तक का उपयोग कर):

.method private hidebysig static int32 Test() cil managed 
{ 
    .maxstack 1 
    .locals init (
     [0] int32 CS$1$0000) 
    L_0000: call int32 Program::SomeNumber() 
    L_0005: stloc.0 
    L_0006: leave.s L_000e 
    L_0008: call void Program::Foo() 
    L_000d: endfinally 
    L_000e: ldloc.0 
    L_000f: ret 
    .try L_0000 to L_0008 finally handler L_0008 to L_000e 
} 

यह:

static int Test() { 
    try { 
     return SomeNumber(); 
    } finally { 
     Foo(); 
    } 
} 

को संकलित मूल रूप से एक स्थानीय चर (CS$1$0000) घोषित करता है, वैरिएबल में मान रखता है (i नाइडल हैंडल ब्लॉक), फिर ब्लॉक को बाहर करने के बाद वेरिएबल लोड करता है, फिर इसे वापस कर देता है। परावर्तक renders इस रूप में:

private static int Test() 
{ 
    int CS$1$0000; 
    try 
    { 
     CS$1$0000 = SomeNumber(); 
    } 
    finally 
    { 
     Foo(); 
    } 
    return CS$1$0000; 
} 
+8

यह बिल्कुल ठीक नहीं है ocdedio ने कहा: अंत में वापसी मूल्य की गणना के बाद और वास्तव में funczion से वापस आने से पहले निष्पादित किया जाता है ??? – mmmmmmmm

+5

मुझे लगता है कि नीचे जॉन स्कीट का उत्तर अधिक स्पष्ट और सटीक है। –

+0

"अपवाद-नियंत्रित ब्लॉक" मुझे लगता है कि इस परिदृश्य में अपवाद और अपवाद हैंडलिंग के साथ कुछ लेना देना नहीं है। यह इस बारे में है कि .NET अंततः _resource_ गार्ड निर्माण को लागू करता है। –

18

अंतिम खंड वापसी विवरण के बाद निष्पादित करता है लेकिन वास्तव में फ़ंक्शन से लौटने से पहले। मुझे लगता है कि थ्रेड सुरक्षा के साथ इसका कोई संबंध नहीं है। यह एक हैक नहीं है - आखिर में हमेशा कोशिश करने की गारंटी है कि आप अपने प्रयास ब्लॉक या अपने कैच ब्लॉक में क्या करते हैं।

4

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

मैं केवल यह कर रहा हूं यदि आप वापसी के बाद किसी क्षेत्र के मूल्य में परिवर्तन की गारंटी देना चाहते हैं (और वापसी मूल्य निर्धारित होने के बाद)।

+0

जब तक स्थानीय चर एक प्रतिनिधि द्वारा भी कब्जा नहीं किया जाता है :) –

+0

उम्म, तब क्या होता है? –

+0

फिर एक बंद है, और वस्तु को कचरा नहीं बनाया जा सकता है, क्योंकि अभी भी एक संदर्भ है। – recursive

305

आखिरकार कथन निष्पादित किया गया है, लेकिन वापसी मूल्य प्रभावित नहीं है। निष्पादन आदेश है:

  1. कोड से पहले वापसी कथन वापसी बयान में
  2. अभिव्यक्ति निष्पादित किया जाता है मूल्यांकन किया जाता है
  3. अंत में ब्लॉक निष्पादित होता है
  4. परिणाम में कदम 2

दिया जाता है यहाँ का मूल्यांकन प्रदर्शित करने के लिए एक संक्षिप्त कार्यक्रम:

using System; 

class Test 
{ 
    static string x; 

    static void Main() 
    { 
     Console.WriteLine(Method()); 
     Console.WriteLine(x); 
    } 

    static string Method() 
    { 
     try 
     { 
      x = "try"; 
      return x; 
     } 
     finally 
     { 
      x = "finally"; 
     } 
    } 
} 

यह प्रिंट "कोशिश करें" (क्योंकि वह वापस आ गया है) और फिर "आखिरकार" क्योंकि यह x का नया मान है।

बेशक, अगर हम एक म्यूटेबल ऑब्जेक्ट (उदा।एक स्ट्रिंगबिल्डर) तो आखिरकार ब्लॉक में ऑब्जेक्ट में किए गए कोई भी बदलाव बदले में दिखाई देंगे - इससे रिटर्न वैल्यू को प्रभावित नहीं हुआ है (जो सिर्फ एक संदर्भ है)।

+0

मैं पूछना चाहूँगा, वहाँ दृश्य स्टूडियो में किसी भी विकल्प निष्पादन के समय लिखा सी # कोड के लिए उत्पन्न मध्यवर्ती भाषा (IL) को देखने के लिए है .... –

+0

अपवाद को "हमारे पास एक अस्थायी के लिए एक संदर्भ के लिए लौट रहे है, तो ऑब्जेक्ट (उदाहरण के लिए एक स्ट्रिंगबिल्डर) तो आखिरकार ब्लॉक में ऑब्जेक्ट में किए गए कोई भी बदलाव रिटर्न पर दिखाई देंगे "अगर स्ट्रिंगबिल्डर ऑब्जेक्ट अंततः ब्लॉक में शून्य हो गया है, तो इस मामले में एक गैर-शून्य ऑब्जेक्ट लौटाया जाता है। – Nick

+4

@ निक: यह * ऑब्जेक्ट * में कोई बदलाव नहीं है - यह * परिवर्तनीय * में एक बदलाव है। यह ऑब्जेक्ट को प्रभावित नहीं करता है कि चर के पिछले मान को संदर्भित किया गया है। तो नहीं, यह एक अपवाद नहीं है। –

10

मार्क ग्रेवेल और जॉन स्कीट द्वारा दिए गए उत्तरों पर जोड़ना, वस्तुओं को नोट करना महत्वपूर्ण है और अन्य संदर्भ प्रकार लौटने पर समान व्यवहार करते हैं लेकिन कुछ मतभेद हैं।

"क्या" है कि वापस आ जाता है उसी तर्क के रूप में इस सरल प्रकार:

class Test { 
    public static Exception AnException() { 
     Exception ex = new Exception("Me"); 
     try { 
      return ex; 
     } finally { 
      // Reference unchanged, Local variable changed 
      ex = new Exception("Not Me"); 
     } 
    } 
} 

संदर्भ लौटाए जा रही है से पहले स्थानीय चर में अंत में ब्लॉक एक नया संदर्भ असाइन किया गया है पहले से ही मूल्यांकन किया गया है।

निष्पादन अनिवार्य है:

class Test { 
    public static Exception AnException() { 
     Exception ex = new Exception("Me"); 
     Exception CS$1$0000 = null; 
     try { 
      CS$1$0000 = ex; 
     } finally { 
      // Reference unchanged, Local variable changed 
      ex = new Exception("Not Me"); 
     } 
     return CS$1$0000; 
    } 
} 

अंतर यह अभी भी वस्तु जो अनपेक्षित व्यवहार में परिणाम कर सकते अगर आप सावधान नहीं हैं के गुणों/तरीकों का उपयोग कर परिवर्तनशील प्रकार संशोधित करने के लिए संभव हो जाएगा है।

class Test2 { 
    public static System.IO.MemoryStream BadStream(byte[] buffer) { 
     System.IO.MemoryStream ms = new System.IO.MemoryStream(buffer); 
     try { 
      return ms; 
     } finally { 
      // Reference unchanged, Referenced Object changed 
      ms.Dispose(); 
     } 
    } 
} 

कोशिश-वापसी के बारे में विचार करने के लिए दूसरी बात यह है कि अंततः "संदर्भ द्वारा" पारित पैरामीटर को रिटर्न के बाद संशोधित किया जा सकता है। केवल वापसी मूल्य का मूल्यांकन किया गया है और लौटाए जाने वाले अस्थायी चर में संग्रहीत किया गया है, किसी अन्य चर को अभी भी सामान्य तरीके से संशोधित किया गया है। आखिरकार इस तरह से अवरुद्ध होने तक आउट पैरामीटर का अनुबंध अनुपलब्ध हो सकता है।

class ByRefTests { 
    public static int One(out int i) { 
     try { 
      i = 1; 
      return i; 
     } finally { 
      // Return value unchanged, Store new value referenced variable 
      i = 1000; 
     } 
    } 

    public static int Two(ref int i) { 
     try { 
      i = 2; 
      return i; 
     } finally { 
      // Return value unchanged, Store new value referenced variable 
      i = 2000; 
     } 
    } 

    public static int Three(out int i) { 
     try { 
      return 3; 
     } finally { 
      // This is not a compile error! 
      // Return value unchanged, Store new value referenced variable 
      i = 3000; 
     } 
    } 
} 

किसी अन्य प्रवाह का निर्माण की तरह "की कोशिश-रिटर्न-अंत में" अपनी जगह है और क्लीनर संरचना यह वास्तव में को संकलित लिखने की तुलना में कोड तलाश के लिए अनुमति दे सकते हैं। लेकिन गेटचा से बचने के लिए इसे सावधानी से इस्तेमाल किया जाना चाहिए।

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