यदि अंत में एक अपवाद फेंकता है तो क्या होता है?


266

यदि अंत में एक अपवाद को फेंकता है, तो वास्तव में क्या होता है?

विशेष रूप से, क्या होता है अगर अपवाद को अंत में ब्लॉक के माध्यम से बीच में फेंक दिया जाता है। क्या इस ब्लॉक में शेष विवरण (बाद में) प्राप्त होते हैं?

मुझे पता है कि अपवाद ऊपर की ओर फैलेंगे।


8
सिर्फ कोशिश क्यों नहीं की? लेकिन इस तरह की चीजों पर, मुझे जो सबसे ज्यादा पसंद है, वह अंत में पहले लौटता है और फिर अंत में ब्लॉक से कुछ और लौटाता है। :)
एनेव्स

8
अंततः ब्लॉक में सभी कथनों को निष्पादित करना होगा। इसका प्रतिफल नहीं हो सकता। msdn.microsoft.com/en-us/library/0hbbzekw(VS.80).aspx
टिम स्कारबोरो

जवाबों:


419

अगर एक अंत में एक अपवाद फेंकता है तो वास्तव में क्या होता है?

यह अपवाद बाहर और ऊपर प्रचारित होता है, और उच्च स्तर पर वसीयत (कैन) को संभाला जा सकता है।

आपका आखिरकार ब्लॉक नहीं होगा उस बिंदु से परे पूरा जहां अपवाद फेंका गया है।

यदि अंत में ब्लॉक पहले के अपवाद से निपटने के दौरान निष्पादित हो रहा था, तो वह पहला अपवाद खो गया है।

C # 4 भाषा विशिष्टता .5 8.9.5: यदि अंत में ब्लॉक एक और अपवाद फेंकता है, तो वर्तमान अपवाद का प्रसंस्करण समाप्त हो जाता है।


9
जब तक यह एक है ThreadAbortException, तब पूरे अंत में ब्लॉक पहले समाप्त हो जाएगा, क्योंकि यह एक महत्वपूर्ण खंड है।
Dmytro Shevchenko

1
@ शेडल - आप सही कह रहे हैं लेकिन यह केवल "कुछ अतुल्यकालिक अपवादों" पर लागू होता है, यानी थ्रेडबोर्टएक्ससेप्शन। सामान्य 1-थ्रेड कोड के लिए मेरा उत्तर है।
हेंक होल्टरमैन

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

"मुझे डिस्पोजेबल ऑब्जेक्ट्स मिलते हैं जो अपवाद () में अपवाद फेंकते हैं" - यह कम से कम कहने के लिए अजीब है। MSDN पर पढ़ें: AVOID डिसपोज़ (बूल) के भीतर से एक अपवाद को छोड़कर ... के अलावा
हेनक होल्टरमैन

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

101

इन जैसे सवालों के लिए मैं आमतौर पर विज़ुअल स्टूडियो में एक खाली कंसोल एप्लिकेशन प्रोजेक्ट खोलता हूं और एक छोटा सा नमूना कार्यक्रम लिखता हूं:

using System;

class Program
{
    static void Main(string[] args)
    {
        try
        {
            try
            {
                throw new Exception("exception thrown from try block");
            }
            catch (Exception ex)
            {
                Console.WriteLine("Inner catch block handling {0}.", ex.Message);
                throw;
            }
            finally
            {
                Console.WriteLine("Inner finally block");
                throw new Exception("exception thrown from finally block");
                Console.WriteLine("This line is never reached");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Outer catch block handling {0}.", ex.Message);
        }
        finally
        {
            Console.WriteLine("Outer finally block");
        }
    }
}

जब आप प्रोग्राम चलाते हैं तो आपको सही क्रम दिखाई देगा catch और finallyब्लॉक निष्पादित होते हैं। कृपया ध्यान दें कि अपवाद को फेंके जाने के बाद अंततः ब्लॉक में कोड निष्पादित नहीं किया जाएगा (वास्तव में, इस नमूना कार्यक्रम में विजुअल स्टूडियो आपको चेतावनी भी देगा कि यह अनुपलब्ध कोड का पता चला है):

इनर ब्लॉक ब्लॉक हैंडलिंग अपवाद ट्राई ब्लॉक से फेंका गया।
अंत में ब्लॉक
अंत में ब्लॉक से फेंका गया बाहरी पकड़ ब्लॉक हैंडलिंग अपवाद।
बाहरी अंत में ब्लॉक

अतिरिक्त टिप्पणी

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

using System;

class Program
{
    static void Main(string[] args)
    {
        try
        {
            try
            {
                throw new Exception("exception thrown from try block");
            }
            finally
            {
                Console.WriteLine("Inner finally block");
                throw new Exception("exception thrown from finally block");
                Console.WriteLine("This line is never reached");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Outer catch block handling {0}.", ex.Message);
        }
        finally
        {
            Console.WriteLine("Outer finally block");
        }
    }
}

जैसा कि आप आउटपुट से देख सकते हैं कि आंतरिक अपवाद "खोया" है (यानी अनदेखा किया गया है):

अंत में ब्लॉक
अंत में ब्लॉक से फेंका गया बाहरी पकड़ ब्लॉक हैंडलिंग अपवाद।
बाहरी अंत में ब्लॉक

2
क्योंकि आप अपने भीतर की पकड़ में अपवाद हैं, 'इनर अंत में ब्लॉक' इस उदाहरण में कभी नहीं
पहुंचेगा

4
@ Theofanis Pantelides: नहीं, एक finallyब्लॉक (लगभग) हमेशा निष्पादित किया जाएगा, यह अंत में ब्लॉक के लिए इस मामले में भी आयोजित होता है (बस नमूना कार्यक्रम को स्वयं आज़माएं (गैर-पुनर्प्राप्त करने के मामले में अंत में ब्लॉक को निष्पादित नहीं किया जाएगा) अपवाद, उदाहरण के लिए EngineExecutionException, लेकिन इस तरह के मामले में आपका कार्यक्रम वैसे भी तुरंत समाप्त हो जाएगा)
डर्क वोल्मार

1
मैं यह नहीं देखता कि आपके पहले कोड ऑफ कोड के पहले कैच में थ्रो की क्या भूमिका है। मैंने कंसोल एप्लिकेशन के साथ और इसके बिना प्रयास किया, कोई भिन्नता नहीं मिली।
जॉनपैन

@ जोह्नपैन: बिंदु यह दिखाना था कि अंत में ब्लॉक हमेशा निष्पादित होता है, भले ही ब्लॉक को पकड़ने की कोशिश करें और पकड़ें। कंसोल आउटपुट में वास्तव में कोई अंतर नहीं है।
डर्क वोल्मार

10

यदि कोई अपवाद लंबित है (जब tryब्लॉक में एक finallyलेकिन नहीं है catch), तो नया अपवाद प्रतिस्थापित करता है।

यदि कोई अपवाद लंबित नहीं है, तो यह बाहर अपवाद फेंकने के रूप में काम करता है finally ब्लॉक के ।


एक मिलान ब्लॉक होने पर अपवाद भी लंबित हो सकता हैcatch जो (पुनः) एक अपवाद को फेंकता है।
stakx - अब


3

त्वरित (और बल्कि स्पष्ट) स्निपेट "मूल अपवाद" ( tryब्लॉक में फेंक दिया गया) को बचाने के लिए और "अंततः अपवाद" (ब्लॉक में फेंक दिया गया ) का त्याग करें finally, यदि आपके लिए मूल एक और अधिक महत्वपूर्ण है:

try
{
    throw new Exception("Original Exception");
}
finally
{
    try
    {
        throw new Exception("Finally Exception");
    }
    catch
    { }
}

जब ऊपर कोड निष्पादित होता है, तो "मूल अपवाद" कॉल स्टैक को प्रचारित करता है, और "अंत में अपवाद" खो जाता है।


2

मुझे एक धारा को बंद करने की कोशिश में एक त्रुटि को पकड़ने के लिए ऐसा करना पड़ा जो एक अपवाद के कारण कभी नहीं खोला गया था।

errorMessage = string.Empty;

try
{
    byte[] requestBytes = System.Text.Encoding.ASCII.GetBytes(xmlFileContent);

    webRequest = WebRequest.Create(url);
    webRequest.Method = "POST";
    webRequest.ContentType = "text/xml;charset=utf-8";
    webRequest.ContentLength = requestBytes.Length;

    //send the request
    using (var sw = webRequest.GetRequestStream()) 
    {
        sw.Write(requestBytes, 0, requestBytes.Length);
    }

    //get the response
    webResponse = webRequest.GetResponse();
    using (var sr = new StreamReader(webResponse.GetResponseStream()))
    {
        returnVal = sr.ReadToEnd();
        sr.Close();
    }
}
catch (Exception ex)
{
    errorMessage = ex.ToString();
}
finally
{
    try
    {
        if (webRequest.GetRequestStream() != null)
            webRequest.GetRequestStream().Close();
        if (webResponse.GetResponseStream() != null)
            webResponse.GetResponseStream().Close();
    }
    catch (Exception exw)
    {
        errorMessage = exw.ToString();
    }
}

अगर webRequest बनाया गया था, लेकिन एक कनेक्शन त्रुटि के दौरान हुआ

using (var sw = webRequest.GetRequestStream())

तब अंत में एक अपवाद को पकड़ना होगा जो कनेक्शन को बंद करने की कोशिश कर रहा था जो सोचा गया था कि खुला था क्योंकि वेबरेज़ेस्ट बनाया गया था।

अगर अंत में अंदर की कोशिश नहीं होती, तो यह कोड webRequest को साफ करते समय एक अनहेल्दी अपवाद का कारण होगा

if (webRequest.GetRequestStream() != null) 

वहाँ से कोड ठीक से उस त्रुटि को संभालने के बिना बाहर निकल जाएगा और इसलिए कॉलिंग विधि के लिए समस्याएँ पैदा कर रहा है।

आशा है कि यह एक उदाहरण के रूप में मदद करता है


1

एक अपवाद को फेंकते समय एक और अपवाद सक्रिय है, जिसके परिणामस्वरूप पहला अपवाद दूसरे (बाद में) अपवाद से प्रतिस्थापित हो जाएगा।

यहाँ कुछ कोड है जो दिखाता है कि क्या होता है:

    public static void Main(string[] args)
    {
        try
        {
            try
            {
                throw new Exception("first exception");
            }
            finally
            {
                //try
                {
                    throw new Exception("second exception");
                }
                //catch (Exception)
                {
                    //throw;
                }
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
        }
    }
  • कोड चलाएं और आपको "दूसरा अपवाद" दिखाई देगा
  • कोशिश और बयानों को पकड़ें और आप "पहला अपवाद" देखेंगे
  • थ्रो को भी अनफ्रेंड करें; बयान और आप फिर से "दूसरा अपवाद" देखेंगे।

यह ध्यान देने योग्य है कि यह एक "गंभीर" अपवाद की सफाई के लिए संभव है जो केवल एक विशेष कोड ब्लॉक के बाहर पकड़ा जाएगा एक अपवाद फेंकने के लिए जो इसे पकड़ा गया है और इसके भीतर संभाला गया है। अपवाद फ़िल्टर का उपयोग करना (vb.net में उपलब्ध है, हालांकि C # नहीं) इस स्थिति का पता लगाना संभव है। एक पूरी तरह से ऐसा कोड नहीं है जो कोड इसे "हैंडल" करने के लिए कर सकता है, हालांकि अगर कोई किसी भी प्रकार के लॉगिंग फ्रेमवर्क का उपयोग कर रहा है, तो यह लगभग निश्चित रूप से लॉगिंग के लायक है। सफाई के भीतर होने वाले अपवाद होने का C ++ दृष्टिकोण एक सिस्टम मेल्टडाउन है, जो बदसूरत है, लेकिन अपवाद गायब होने से IMEs सुरक्षित है।
सुपरकैट

1

कुछ महीने पहले मैंने भी कुछ इस तरह का सामना किया था,

    private  void RaiseException(String errorMessage)
    {
        throw new Exception(errorMessage);
    }

    private  void DoTaskForFinally()
    {
        RaiseException("Error for finally");
    }

    private  void DoTaskForCatch()
    {
        RaiseException("Error for catch");
    }

    private  void DoTaskForTry()
    {
        RaiseException("Error for try");
    }


        try
        {
            /*lacks the exception*/
            DoTaskForTry();
        }
        catch (Exception exception)
        {
            /*lacks the exception*/
            DoTaskForCatch();
        }
        finally
        {
            /*the result exception*/
            DoTaskForFinally();
        }

इस तरह की समस्या को हल करने के लिए मैंने एक उपयोगिता वर्ग बनाया

class ProcessHandler : Exception
{
    private enum ProcessType
    {
        Try,
        Catch,
        Finally,
    }

    private Boolean _hasException;
    private Boolean _hasTryException;
    private Boolean _hasCatchException;
    private Boolean _hasFinnallyException;

    public Boolean HasException { get { return _hasException; } }
    public Boolean HasTryException { get { return _hasTryException; } }
    public Boolean HasCatchException { get { return _hasCatchException; } }
    public Boolean HasFinnallyException { get { return _hasFinnallyException; } }
    public Dictionary<String, Exception> Exceptions { get; private set; } 

    public readonly Action TryAction;
    public readonly Action CatchAction;
    public readonly Action FinallyAction;

    public ProcessHandler(Action tryAction = null, Action catchAction = null, Action finallyAction = null)
    {

        TryAction = tryAction;
        CatchAction = catchAction;
        FinallyAction = finallyAction;

        _hasException = false;
        _hasTryException = false;
        _hasCatchException = false;
        _hasFinnallyException = false;
        Exceptions = new Dictionary<string, Exception>();
    }


    private void Invoke(Action action, ref Boolean isError, ProcessType processType)
    {
        try
        {
            action.Invoke();
        }
        catch (Exception exception)
        {
            _hasException = true;
            isError = true;
            Exceptions.Add(processType.ToString(), exception);
        }
    }

    private void InvokeTryAction()
    {
        if (TryAction == null)
        {
            return;
        }
        Invoke(TryAction, ref _hasTryException, ProcessType.Try);
    }

    private void InvokeCatchAction()
    {
        if (CatchAction == null)
        {
            return;
        }
        Invoke(TryAction, ref _hasCatchException, ProcessType.Catch);
    }

    private void InvokeFinallyAction()
    {
        if (FinallyAction == null)
        {
            return;
        }
        Invoke(TryAction, ref _hasFinnallyException, ProcessType.Finally);
    }

    public void InvokeActions()
    {
        InvokeTryAction();
        if (HasTryException)
        {
            InvokeCatchAction();
        }
        InvokeFinallyAction();

        if (HasException)
        {
            throw this;
        }
    }
}

और इस तरह इस्तेमाल किया

try
{
    ProcessHandler handler = new ProcessHandler(DoTaskForTry, DoTaskForCatch, DoTaskForFinally);
    handler.InvokeActions();
}
catch (Exception exception)
{
    var processError = exception as ProcessHandler;
    /*this exception contains all exceptions*/
    throw new Exception("Error to Process Actions", exception);
}

लेकिन अगर आप पैरामाटर्स का उपयोग करना चाहते हैं और वापसी के प्रकार जो एक दूसरी कहानी है


1
public void MyMethod()
{
   try
   {
   }
   catch{}
   finally
   {
      CodeA
   }
   CodeB
}

कोडा और कोडबी द्वारा फेंके गए अपवादों को जिस तरह से संभाला जाता है, वही है।

finallyब्लॉक में फेंके गए अपवाद में कुछ विशेष नहीं है, इसे कोड बी द्वारा अपवाद फेंक के रूप में माना जाता है।


क्या आप विस्तृत कर सकते हैं? अपवादों के साथ आपका क्या मतलब है?
डर्क वोल्मार

1

अपवाद प्रचारित करता है, और इसे उच्च स्तर पर नियंत्रित किया जाना चाहिए। यदि अपवाद को उच्च स्तर पर नियंत्रित नहीं किया जाता है, तो अनुप्रयोग क्रैश हो जाता है। "अंत" ब्लॉक निष्पादन उस बिंदु पर बंद हो जाता है जहां अपवाद फेंक दिया जाता है।

चाहे कोई अपवाद हो या नहीं "अंत में" ब्लॉक निष्पादित करने की गारंटी है।

  1. यदि "अंततः" ब्लॉक को ब्लॉक ब्लॉक में अपवाद के बाद निष्पादित किया जा रहा है,

  2. और अगर उस अपवाद को नियंत्रित नहीं किया जाता है

  3. और अगर अंत में एक अपवाद फेंकता है

फिर कोशिश ब्लॉक में होने वाला मूल अपवाद खो गया है।

public class Exception
{
    public static void Main()
    {
        try
        {
            SomeMethod();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

    public static void SomeMethod()
    {
        try
        {
            // This exception will be lost
            throw new Exception("Exception in try block");
        }
        finally
        {
            throw new Exception("Exception in finally block");
        }
    }
} 

विवरण के लिए महान लेख


-1

यह एक अपवाद फेंकता है;) आप उस अपवाद को कुछ अन्य कैच क्लाज में पकड़ सकते हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.