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

Если блок finally генерирует исключение, что означает точно?

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

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

Ответ 1

Если блок finally генерирует исключение, что происходит точно?

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

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

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

С# 4 Language Specification & sect; 8.9.5: Если блок finally выдает другое исключение, обработка текущего исключения прекращается.

Ответ 2

Для таких вопросов я обычно открываю пустой проект консольного приложения в 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

Ответ 3

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

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

Ответ 4

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

Ответ 5

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

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.

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

if (webRequest.GetRequestStream() != null) 

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

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

Ответ 6

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

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

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

Ответ 7

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

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

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

Ответ 8

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

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

    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 и вы увидите "первое исключение"
  • Также раскомментируйте бросок; и вы снова увидите "второе исключение".

Ответ 9

Несколько месяцев назад я также столкнулся с чем-то вроде этого,

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

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

Ответ 10

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

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

  • Если блок "finally" выполняется после того, как в блоке try произошел исключение,

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

  • и если блок 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");
        }
    }
} 

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

Ответ 11

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