Что произойдет, если блок finally бросает исключение?
Если блок finally выдает исключение, что ровно происходит?
в частности, что происходит, если исключение выбрасывается на полпути через блок finally. Вызываются ли остальные операторы (после) в этом блоке?
Я знаю, что исключения будут распространяться вверх.
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" гарантированно выполняется.
Если блок" finally " выполняется после возникновения исключения в попытке блок
и если это исключение не обрабатывается
и если блок 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"); } } }
Comments