如果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"); } } }
当你运行程序时,你会看到执行catch
和finally
块的确切顺序。 请注意,在引发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”块都保证执行。
-
如果在try块中发生exception之后,正在执行“finally”块,
-
如果这个exception没有处理
-
如果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。