2012-06-25 22 views
16

प्रश्न: सी # में सादे throw कथन क्या कभी भी एक नया अपवाद पैदा कर सकता है?सी # कारण अपवादों में (सादा) फेंक स्टेटमेंट कर सकते हैं?


ध्यान दें कि मैं जिज्ञासा से बाहर यह सवाल पूछते, इसलिए नहीं कि मैं किसी भी व्यावहारिक या वास्तविक दुनिया स्थिति है जहाँ यह बहुत फर्क होता है। यह भी ध्यान रखें कि मेरा आंत महसूस और अनुभव मुझे बताता है कि उत्तर "नहीं" है, लेकिन मैं किसी भी तरह से उस उत्तर को सत्यापित करने के लिए देख रहा हूं (अब तक मैंने कोशिश की गई स्रोतों पर और देखें)।

यहाँ मेरे सवाल का वर्णन करने के लिए कुछ नमूना कोड:

try 
{ 
    int x = 0, y = 1/x; 
} 
catch (Exception outerException) 
{ 

    try 
    { 
     throw; 
    } 
    catch (Exception innerException) 
    { 
     // Q: Does this Assert ever fail?? 
     System.Diagnostics.Debug.Assert(outerException.Equals(innerException)); 
    } 
} 

मैं वहाँ किसी भी तरह से है कि अगर सब पर, परिस्थितियों ऐसी है कि Assert असफल हो जायेगी को बदलने के लिए भीतरी try/catch ब्लॉक को छुए बिना सोच रहा हूँ।

मैं क्या कोशिश की है या यह जवाब देने की कोशिश करने के लिए देख रहा था:

  • पढ़ें MSDN पर throw (C# Reference) पेज - कोई निश्चित जवाब;
  • C# Language Specification की जांच की गई 5.3.3.11 - जो शायद इस तरह की जानकारी देखने के लिए गलत जगह है;
  • अपवादों के माध्यम से चमक गया कि मैं फेंक कथन पर ट्रिगर करने का प्रयास कर सकता हूं। OutOfMemoryException दिमाग में आता है, लेकिन throw के समय ट्रिगर करना मुश्किल है।
  • जेनरेट कोड की जांच करने के लिए ILDASM खोल दिया। मैं देख सकता हूं कि throw एक rethrow निर्देश में अनुवाद करता है, लेकिन मुझे यह पता लगाने के लिए और कहां खोना है कि यह कथन अपवाद फेंक सकता है या नहीं।

यह वही है ILDASM भीतरी try बिट के लिए पता चलता है:

.try 
{ 
    IL_000d: nop 
    IL_000e: rethrow 
} // end .try 

तो, संक्षेप में प्रस्तुत करने के लिए: एक फेंक बयान कभी कारण एक अपवाद ही (एक अपवाद rethrow करते थे) कर सकते हैं?

+6

संतोषजनक लोगों को संतुष्ट करने के अलावा, आप किस समस्या को हल करने की कोशिश कर रहे हैं? – Oded

+2

बस जिज्ञासा मुझे डर है, मेरे पास कोई रोमांचक नहीं है (जहां तक ​​अपवाद भी रोमांचक हो सकता है) इससे संबंधित वास्तविक दुनिया परिदृश्य। – Jeroen

+0

मुझे लगता है कि अगर आपका ढेर दूषित हो तो ऐसा हो सकता है। उस स्थिति में रीथ्रो कैच/आखिरकार/गलती खंड के साथ अगली विधि में स्टैक को खोलने का प्रयास करेगा। यदि यह नहीं हो सकता है तो यह अपवाद का कारण बन जाएगा लेकिन अधिकतर एक्जिक्यूशनइंजिन एक्सेप्शन जो आमतौर पर आपकी प्रक्रिया को तत्काल समाप्त कर देगा। –

उत्तर

7

क्या सी # में सादा फेंक स्टेटमेंट कभी भी एक नया अपवाद पैदा कर सकता है?

परिभाषा के अनुसार यह नहीं होगा। throw; का बहुत ही बिंदु सक्रिय अपवाद (विशेष रूप से स्टैक-ट्रेस) को संरक्षित करना है।

सैद्धांतिक रूप से एक कार्यान्वयन अपवाद को क्लोन कर सकता है लेकिन बिंदु क्या होगा?

+0

उत्तर देने का समय लेने के लिए धन्यवाद! * "परिभाषा के अनुसार यह नहीं होगा" * वही था जो मैं उम्मीद कर रहा था, लेकिन जैसा कि मैंने अपने प्रश्न में लिखा था, मुझे इस पर कोई संदेह नहीं है (इस पर) एक परिभाषा है जो इस पर मेरे संदेह को खत्म करती है। इसके अलावा, प्रश्न पर @AloisKraus 'टिप्पणी से पता चलता है कि फेंकने पर अपवाद प्राप्त करना अभी भी संभव हो सकता है। शायद किसी के पास एक संदर्भ है जो मदद कर सकता है? – Jeroen

+0

मुझे लगता है कि एलोइस गेबे द्वारा एक सुझाव को अस्वीकार कर रहा है। यह अभी भी स्पष्ट नहीं है कि आप क्या खोज रहे हैं। –

+0

आह क्षमा करें, आप टिप्पणी पर सही हैं, मेरे बुरे! – Jeroen

1

आपका दावा कभी विफल नहीं होगा क्योंकि पुनर्विचार और दावे के बीच कोई कोड नहीं है। यदि आप अपवाद पकड़ते हैं और दूसरा कारण बनते हैं तो एक अपवाद बदलता है - उदाहरण के लिए। अपने पकड़ खंड में बग्गी कोड या "नया फेंक" करके।

+0

मुझे पता है कि रेथ्रो और दावे के बीच कोई कोड नहीं है। यह उद्देश्य पर है, क्योंकि मेरा सवाल यह है कि क्या 'फेंक' स्वयं अपवाद का कारण बन सकता है। मैं इसे थोड़ा सा स्पष्ट करने के लिए अपने प्रश्न का प्रयास और अद्यतन करूंगा। – Jeroen

+0

कोई सार्थक 'प्रबंधित' कोड नहीं है लेकिन सीएलआर को फिर से फेंकने के लिए कुछ कोड चलाने पड़ते हैं, जिसके परिणामस्वरूप कुछ अपवाद हो सकता है। –

15

मेरी ईमानदार राय में, सैद्धांतिक रूप से जोर 'असफल' हो सकता है (व्यावहारिक रूप से मुझे ऐसा नहीं लगता)।

कैसे?

नोट: एसएससीएलआई पर पहले किए गए कुछ शोधों के आधार पर नीचे मेरी 'राय' है।

  1. एक InvalidProgramException हो सकता है। यह स्वीकार्य रूप से अत्यधिक असंभव है लेकिन फिर भी सैद्धांतिक रूप से संभव है (उदाहरण के लिए कुछ आंतरिक सीएलआर त्रुटि के कारण फेंकने योग्य वस्तु अनुपलब्ध हो सकती है !!!!)।
  2. यदि सीएलआर को 'री-थ्रो' एक्शन को संसाधित करने के लिए पर्याप्त मेमोरी नहीं मिलती है तो यह इसके बजाय आउटऑफमेमरी अपवाद फेंक देगा (सीएलआर के आंतरिक री-थ्रो लॉजिक को कुछ मेमोरी आवंटित करने की आवश्यकता है यदि यह 'प्री-आवंटित' अपवादों से निपट नहीं रहा है OutOfMemoryException)।
  3. यदि सीएलआर किसी अन्य होस्ट (उदाहरण के लिए एसक्यूएल सर्वर या यहां तक ​​कि अपने स्वयं के) के तहत चल रहा है और मेजबान अपवाद पुनः थ्रो थ्रेड (कुछ आंतरिक तर्क के आधार पर) को समाप्त करने का निर्णय लेता है ThreadAbortException (अशिष्ट धागा abort के रूप में जाना जाता है) इस मामले में) उठाया जाएगा। हालांकि, मुझे यकीन नहीं है कि अगर इस मामले में कथन भी निष्पादित होगा।
  4. कस्टम होस्ट ने सीएलआर (ICLRPolicyManager::SetActionOnFailure) में वृद्धि नीति लागू की हो सकती है। उस स्थिति में यदि आप OutOfMemoryException से निपट रहे हैं, तो वृद्धि नीति थ्रेडएबॉर्ट अपवाद होने का कारण बन सकती है (फिर से अशिष्ट धागा निरस्त। सुनिश्चित नहीं है कि नीति सामान्य थ्रेड निरस्त करने के लिए क्या होती है)।
  5. हालांकि @ एलोइस क्रॉस स्पष्ट करता है कि एसएससीएलआई शोध से 'सामान्य' धागा निरस्त अपवाद संभव नहीं है, मुझे अभी भी संदेह है कि (सामान्य) थ्रेडएबॉर्ट अपवाद हो सकता है।

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

जैसा कि मैंने पहले कहा कि ज़ोर सैद्धांतिक रूप से असफल हो सकता है लेकिन व्यावहारिक रूप से यह अत्यधिक असंभव है। इसलिए इसके लिए पीओसी विकसित करना बहुत मुश्किल है। अधिक 'साक्ष्य' प्रदान करने के लिए, rethow आईएल निर्देश को संसाधित करने के लिए एसएससीएलआई कोड से स्निपेट निम्नलिखित हैं जो मेरे उपरोक्त बिंदुओं को मान्य करते हैं।

चेतावनी: वाणिज्यिक सीएलआर एसएससीएलआई से बहुत व्यापक रूप से भिन्न हो सकता है।

  1. InvalidProgramException:

    if (throwable != NULL) 
    { 
    ... 
    } 
    else 
    { 
        // This can only be the result of bad IL (or some internal EE failure). 
        RealCOMPlusThrow(kInvalidProgramException, (UINT)IDS_EE_RETHROW_NOT_ALLOWED); 
    } 
    
  2. असभ्य थ्रेड बीच में बंद करें:

    if (pThread->IsRudeAbortInitiated()) 
    { 
        // Nobody should be able to swallow rude thread abort. 
        throwable = CLRException::GetPreallocatedRudeThreadAbortException(); 
    } 
    

    इसका मतलब यह है कि अगर 'अशिष्ट धागा बीच में बंद करें' शुरू हो चुका है, किसी भी अपवाद अशिष्ट धागा करने के लिए बदल जाता है कि अपवाद को निरस्त ।

  3. अब सभी का सबसे दिलचस्प, OutOfMemoryException। चूंकि पुनर्स्थापना आईएल निर्देश अनिवार्य रूप से उसी अपवाद वस्तु को फिर से फेंकता है (यानी object.ReferenceEquals सत्य लौटाता है) ऐसा लगता है कि आउटऑफमेमरी अपवाद फिर से फेंकने पर हो सकता है। हालांकि, निम्नलिखित SSCLI कोड से पता चलता है कि यह संभव है:

    // Always save the current object in the handle so on rethrow we can reuse it. This is important as it 
    // contains stack trace info. 
    // 
    // Note: we use SafeSetLastThrownObject, which will try to set the throwable and if there are any problems, 
    // it will set the throwable to something appropiate (like OOM exception) and return the new 
    // exception. Thus, the user's exception object can be replaced here. 
    
    throwable = pThread->SafeSetLastThrownObject(throwable); 
    

    SafeSetLastThrownObject SetLastThrownObject कॉल करता है और अगर यह विफल OutOfMemoryException को जन्म देती है। यहाँ SetLastThrownObject से स्निपेट (मेरी टिप्पणी के साथ जोड़ा)

    ... 
    if (m_LastThrownObjectHandle != NULL) 
    { 
        // We'll somtimes use a handle for a preallocated exception object. We should never, ever destroy one of 
        // these handles... they'll be destroyed when the Runtime shuts down. 
        if (!CLRException::IsPreallocatedExceptionHandle(m_LastThrownObjectHandle)) 
        { 
        //Destroys the GC handle only but not the throwable object itself 
        DestroyHandle(m_LastThrownObjectHandle); 
        } 
    } 
    ... 
    
    //This step can fail if there is no space left for a new handle 
    m_LastThrownObjectHandle = GetDomain()->CreateHandle(throwable); 
    

    कोड के टुकड़े से ऊपर से पता चलता है कि फेंकने योग्य वस्तु की जीसी संभाल नष्ट हो जाता है (अर्थात जीसी तालिका में एक स्लॉट भी मुक्त हो जाता है) और फिर एक नया संभाल बनाया जाता है।चूंकि एक स्लॉट अभी जारी किया गया है, इसलिए नए हैंडल सृजन कभी भी सही समय पर निर्धारित होने वाले नए थ्रेड के अत्यधिक दुर्लभ परिदृश्य में और सभी उपलब्ध जीसी हैंडल का उपभोग करने तक ऑफ-कोर्स तक असफल रहेगा। इस से

अलावा (rethrows सहित) सभी अपवादों RaiseException जीत एपीआई के माध्यम से उठाए गए हैं। कोड जो इस अपवाद को इसी प्रबंधित अपवाद को तैयार करने के लिए पकड़ता है, वह स्वयं OutOfMemoryException बढ़ा सकता है।

+0

यह उत्तर बहुत अच्छा है, धन्यवाद! आपके कई बिंदुओं पर तर्क तार्किक लगता है, और मैं इसके साथ जाने के इच्छुक हूं। जवाब निश्चित रूप से अधिक निश्चित होगा यदि हम कुछ नमूना ऐप और यह दिखाने के लिए कदम उठा सकते हैं कि यह हो सकता है। मैं आपके परिदृश्य में से किसी एक को बाद में सत्यापित करने का प्रयास कर सकता हूं, और आपको बता सकता हूं कि मुझे क्या मिला। – Jeroen

+0

दिलचस्प संपादन। मुश्किल सवाल, ज़ाहिर है, यह व्यवहार सही और/या अनुपालन है; ई ईमानदारी से, मुझे इसका जवाब नहीं पता है। लेकिन एक अच्छा पढ़ा, चीयर्स। –

+0

यही कारण है कि मैंने यह चेतावनी दी है:) .... हालांकि जहां तक ​​वाणिज्यिक सीएलआर का व्यवहार है, मुझे लगता है कि एक प्रोफाइलर संलग्न करके यह जांचना संभव हो सकता है कि यह पुनर्स्थापित करने के दौरान एक नए हैंडल आवंटित करता है या नहीं, नहीं। –

5

मुझे लगता है बिट से वंचित हो रहे rethrow के लिए विनिर्देश, जो ECMA-335 के भीतर है, विभाजन तृतीय, खंड 4.24 हो सकता है:

4,24 rethrow - वर्तमान अपवाद rethrow

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

अपवाद:
मूल अपवाद फेंक दिया गया है।

(जोर मेरा)

तो हाँ, यह आपके दावे की तरह कल्पना के अनुसार काम करने की गारंटी है लग रहा है। (बेशक यह एक कार्यान्वयन कल्पना इस प्रकार यह सोचते है ...)

सी # विनिर्देश की प्रासंगिकता का खंड 8.9.5 (सी # 4 संस्करण):

कोई अभिव्यक्ति के साथ एक फेंक बयान कर सकते हैं केवल एक कैच ब्लॉक में इस्तेमाल किया जाए, इस मामले में कि कथन उस अपवाद को फिर से फेंक देता है जिसे वर्तमान में उस पकड़ ब्लॉक द्वारा संभाला जा रहा है।

कौन सा फिर से पता चलता है कि मूल अपवाद और केवल कि अपवाद फेंक दिया जाएगा।

(धारा 5.3.3.11 जो आप के लिए सिर्फ निश्चित काम, नहीं throw बयान खुद के व्यवहार के बारे में बात कर रहा है कहा जाता है।)

इस में से कोई भी अमित की अंक, ज़ाहिर है, जो स्थितियां हैं, जिसके लिए कर रहे हैं को अमान्य कर किसी भी जगह में निर्दिष्ट क्या है के दायरे से बाहर कुछ हद तक। (जब मेजबान अतिरिक्त नियम लागू करते हैं, तो उनके लिए एक भाषा विनिर्देशन करना मुश्किल होता है।)

+1

धन्यवाद जॉन, जो मैं खोज रहा था उसके बारे में था। जब तक कि कोई काउंटर-उदाहरण के साथ नहीं आता है तो बाउंटी शायद यहां जायेगा :-) – Jeroen

+0

कुछ मैं यह इंगित करता हूं कि यह सी # भाषा का एक विनिर्देश है। वास्तविक अभ्यास में, यह कार्यान्वयन पर निर्भर करता है। मोनो और .नेट सीएलआर के पास बहुत अलग जवाब हो सकते हैं और वास्तव में, और जानबूझकर, उन वातावरणों में इसका परीक्षण करने का एकमात्र तरीका है। – whiskeyfur

+0

अमित के संपादन के बाद यह चुनना बहुत कठिन लगा कि बक्षीस को कहां दिया जाए। मैं उसे करने के लिए मूल इनाम दिया, और (कारण "योग्य अतिरिक्त क्रेडिट का जवाब"), यह एक के लिए एक और बराबर 100 प्रतिनिधि इनाम करना चाहता था, लेकिन किसी कारण से प्रणाली केवल मुझे 200 से और ऊपर इस समय एक इनाम करने की अनुमति देता , इसलिए जब मैं मोबाइल इंटरनेट बंद कर दूंगा तो मुझे उसमें देखना होगा। किसी भी मामले में: महान जवाब के लिए बहुत धन्यवाद! – Jeroen

0

रिकर्सन सादे throw के साथ संयुक्त 64-बिट प्लेटफ़ॉर्म पर आसानी से StackOverflowException का कारण बन सकता है।

class Program 
{ 
    // expect it to be 10 times less in real code 
    static int max = 455; 

    static void Test(int i) 
    { 
     try { 
      if (i >= max) throw new Exception("done"); 
      Test(i + 1); 
     } 
     catch { 
      Console.WriteLine(i); 
      throw; 
     } 
    } 

    static void Main(string[] args) 
    { 
     try { 
      Test(0); 
     } 
     catch { 
     } 
     Console.WriteLine("Done."); 
    } 
} 

कंसोल में:

... 
2 
1 
0 

Process is terminated due to StackOverflowException. 

कुछ स्पष्टीकरण here पाया जा सकता है।

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