如果finally块引发exception,会发生什么?

如果finally块引发exception, 究竟发生了什么?

具体来说,如果在finally块中途抛出exception,会发生什么情况。 这个块中的其余语句(之后)是否被调用?

我知道exception会向上传播。

如果finally块抛出一个exception, 究竟发生了什么?

这个例外传播出去,并将(可以)在更高层次上处理。

你的finally块将不会超出抛出exception的地方。

如果在处理更早的exception期间finally块正在执行,那么第一个exception就会丢失。

C#4语言规范§8.9.5:如果finally块引发另一个exception,则终止当前exception的处理。

对于像这样的问题,我通常在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"); } } } 

当你运行程序时,你会看到执行catchfinally块的确切顺序。 请注意,在引发exception之后的finally块中的代码将不会被执行(事实上,在这个示例程序中,Visual Studio甚至会警告您已经检测到无法访问的代码):

从try块引发的内部catch catch块处理exception。
内心终于阻止
从finally块中抛出的外部catch catch块处理exception。
外面终于挡住了

额外的备注

正如Michael Damatov所指出的那样,如果你没有在(内部) catch块中处理它, try块中的exception将被“吃掉”。 事实上,在上面的例子中,重新抛出的exception不会出现在外部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"); } } } 

正如你可以从输出中看到的内部exception是“失去”(即忽略):

内心终于阻止
从finally块中抛出的外部catch catch块处理exception。
外面终于挡住了

如果有一个exception挂起(当try块有finally一个但没有catch ),新的exception将取代这个exception。

如果没有exception挂起,就像在finally块外面抛出一个exception一样。

exception被传播。

为了保存“原始exception”(在try块中抛出)并牺牲“finally exception”(抛出finally块),以防万一原来的一个更重要:

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

当执行上面的代码时,“原始exception”向上传播调用堆栈,并且“Finally Exception”丢失。

我不得不这样做,捕捉一个错误试图closures从未打开,因为一个例外的stream。

 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被创build,但在连接错误期间发生

 using (var sw = webRequest.GetRequestStream()) 

那么terminal会捕获一个exception,试图closures它认为已经打开的连接,因为webRequest已经创build。

如果最后没有try-catch里面,这个代码会导致一个未处理的exception,同时清理webRequest

 if (webRequest.GetRequestStream() != null) 

从那里代码会退出,没有正确处理发生的错误,从而导致调用方法的问题。

希望这有助于作为一个例子

在另一个exception处于活动状态时抛出exception将导致第一个exception被第二个(稍后)exception取代。

下面是一些代码,说明发生了什么:

  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); } } 
  • 运行代码,你会看到“第二个exception”
  • 取消注释try和catch语句,您将看到“第一个exception”
  • 也取消投掷的评论; 声明,你会再次看到“第二个exception”。

几个月前我也遇到过这样的事情,

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

但如果你想使用参数和返回types是另一回事

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

CodeA和CodeB引发的exception的处理方式是一样的。

finally块中抛出的exception没有什么特别的,把它当作代码B抛出的exception。

例外传播起来,应该在更高层次上处理。 如果在较高级别处理exception,则应用程序崩溃。 “finally”块的执行停止在抛出exception的地方。

无论是否存在exception,“finally”块都保证执行。

  1. 如果在try块中发生exception之后,正在执行“finally”块,

  2. 如果这个exception没有处理

  3. 如果finally块抛出一个exception

然后,try块中发生的原始exception将丢失。

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

伟大的文章的细节

它会抛出一个exception;)你可以在其他catch子句中捕获这个exception。