Что произойдет, если блок finally бросает исключение?



Если блок finally выдает исключение, что ровно происходит?



в частности, что происходит, если исключение выбрасывается на полпути через блок finally. Вызываются ли остальные операторы (после) в этом блоке?



Я знаю, что исключения будут распространяться вверх.

844   11  

11 ответов:

Если, наконец, блок выдает исключение, что ровно происходит ?

это исключение распространяется и вверх, и будет (может) быть обработаны на более высоком уровне.

ваш finally блок будет не завершается за пределами точки, где возникает исключение.

Если блок finally выполнялся во время обработки более раннего исключения, то это первое исключение теряется.

C# 4 Спецификация языка § 8.9.5: если блок finally создает другое исключение, обработка текущего исключения завершается.

для таких вопросов я обычно открываю пустой проект консольного приложения в Visual Studio и пишу небольшой пример программы:

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 блоки выполняются. Обратите внимание, что код в блоке finally после возникновения исключения не будет выполнен (на самом деле, в этом примере программа Visual Studio даже предупредит вас о том, что она обнаружена недостижимой код):

Inner catch block handling exception thrown from try block.
Inner finally block
Outer catch block handling exception thrown from finally block.
Outer finally block

Дополнительная Информация

как отметил Михаил Даматов, исключение из try блок будет "съеден", если вы не обработаете его в (внутреннем) catch блок. Фактически, в приведенном выше примере повторное исключение не появляется во внешнем блоке 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");
        }
    }
}

как вы можете видеть из вывода внутреннее исключение "потерялся" (т. е. игнорируется):

Inner finally block
Outer catch block handling exception thrown from finally block.
Outer finally block

если есть ожидающее исключение (когда try блока finally а не catch), новое исключение заменяет одна.

если нет никакого исключения в ожидании, он работает так же, как бросать исключение за пределами finally блок.

исключение распространяется.

быстрый (и довольно очевидный) фрагмент для сохранения " оригинального исключения "(брошенного в try блок) и жертвоприношение " наконец исключение "(брошено в finally блок), в случае, если оригинал является более важным для вас:

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

когда код выше выполняется, " исходное исключение "распространяется вверх по стеку вызовов, и" наконец исключение " теряется.

Я должен был сделать это для ловли ошибка при попытке закрыть поток, который никогда не был открыт из-за исключения.

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.

Если, наконец, не было try-catch внутри, этот код вызовет необработанное исключение, пока очистка webRequest

if (webRequest.GetRequestStream() != null) 

оттуда код будет выходить без правильной обработки ошибки, которая произошла и, следовательно, вызывает проблемы для вызывающего метода.

надеюсь, это поможет в качестве примера

выбрасывание исключения в то время как другое исключение активно приведет к тому, что первое исключение будет заменено вторым (более поздним) исключением.

вот некоторый код, который иллюстрирует, что происходит:

    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);
        }
    }
  • запустите код и вы увидите "второе исключение"
  • раскомментируйте операторы try и catch, и вы увидите "первое исключение"
  • также раскомментируйте throw; заявление, и вы увидите "второе исключение" снова.

несколько месяцев назад я тоже столкнулся с чем-то подобным,

    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);
}

но если вы хотите использовать параметры и типы возвращаемых это другая история

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

способ обработки исключений, вызванных CodeA и CodeB, одинаков.

исключение в finally блок не имеет ничего особенного, рассматривайте его как исключение броска кодом B.

исключение распространяется вверх и должно обрабатываться на более высоком уровне. Если исключение не обрабатывается на более высоком уровне, приложение аварийно завершает работу. Выполнение блока "finally" останавливается в точке, где возникает исключение.

независимо от того, есть ли исключение или нет, блок "finally" гарантированно выполняется.

  1. Если блок" finally " выполняется после возникновения исключения в попытке блок

  2. и если это исключение не обрабатывается

  3. и если блок finally выдает исключение

тогда исходное исключение, которое произошло в блоке try, будет потеряно.

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");
        }
    }
} 

отличная статья для деталей

Он выдает исключение ;) вы можете поймать это исключение в каком-то другом предложении catch.

Comments

    Ничего не найдено.