2010-05-26 116 views
225

如果finally块抛出异常,什么恰好发生?如果finally块引发异常,会发生什么情况?

具体来说,如果在finally代码块中途抛出异常,会发生什么情况。请调用此块中的其余语句(之后)吗?

我知道异常会向上传播。

+8

为什么不试试呢?但在这类事情上,我最喜欢的是在finally之前返回,然后从finally块返回其他东西。 :) – ANeves 2010-05-26 09:38:18

+8

必须执行finally块中的所有语句。它不能有回报。 http://msdn.microsoft.com/en-us/library/0hbbzekw(VS.80).aspx – 2010-05-26 12:02:08

回答

348

如果一个finally块抛出异常什么恰好发生?

该异常传播出来,并将(可)在更高层次上处理。

您的finally块将而不是超出抛出异常的位置。

如果finally块在处理先前的异常期间执行,那么第一个异常将丢失。

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

+33

+1:**完全**回答问题的唯一答案 – 2010-05-26 08:30:39

+3

如果从'try'块引发异常,它将被吃掉。 – 2010-05-26 08:36:25

+8

除非它是'ThreadAbortException',否则整个finally块将首先完成,因为它是一个关键部分。 – 2013-10-08 21:23:30

5

传播异常。

+1

@bitbonk:像往常一样从内到外。 – Piskvor 2010-05-26 08:30:39

0

它引发一个异常;)您可以在其他catch子句中捕获该异常。

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

通过CODEa所和CodeB抛出的异常的处理方式是一样的。

finally块抛出的异常有没有什么特别的,把它作为由代码B.

+0

你能否详细说明一下?与例外情况相同的是什么意思? – 2010-05-26 08:36:24

+1

对不起,看到我的编辑 – 2010-05-26 08:41:08

90

异常抛出这样的,我通常开在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块被执行的确切顺序。请注意,该代码在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 

附加注释

正如迈克尔Damatov指出,从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 
+2

因为你抛出你的内部捕获异常,在这个例子中永远不会达到'内部finally块' – 2010-05-26 08:43:52

+3

@Theofanis Pantelides:不,'finally'块将会(几乎)总是被执行,这成立在这种情况下,对于内部finally块(只是自己尝试一下示例程序)(如果发生不可恢复的异常,例如'EngineExecutionException',finally块不会被执行,但在这种情况下,程序将立即终止无论如何) – 2010-05-26 08:59:23

+8

+1:一个比仅仅引用规范更有用的答案 – 2010-05-26 11:11:19

9

如果有异常挂起(当try块具有finally但没有catch),新的异常替换之一。

如果没有未处理的异常,它就像在finally块之外抛出异常一样工作。

+0

如果*有*匹配的catch语句块(重新抛出异常),则异常可能也会挂起。 – stakx 2015-01-19 22:05:36

1

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

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

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语句,你会看到“第一异常”
  • 也取消注释;声明,你会再次看到“第二个异常”。
+0

值得注意的是,清理“严重”异常是可能的,该异常只会在特定代码块之外被捕获,以引发异常并在其中被捕获和处理。使用异常过滤器(在vb.net中可用,但不是C#)可以检测到这种情况。代码可以“处理”它的代码并不多,但如果使用任何类型的日志框架,它几乎肯定值得记录。清理过程中发生异常的C++方法触发系统崩溃很丑陋,但有例外消失是恕我直言可怕的。 – supercat 2012-11-30 16:53:36

1

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

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); 
} 
使用这样的问题

但如果你想使用参数和返回类型是一个其他故事

2

我不得不这样做来捕获一个错误,试图关闭一个从未打开的流,因为有一个异常。

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) 

这个代码将导致未处理的异常从那里代码将退出,不妥善处理所发生的错误,因此造成调用方法的问题。

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

2

快速(和相当明显)段,以拯救“原始异常”(在try块抛出)和牺牲“终于例外”(在finally块抛出),万一原对你更重要:

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

在执行上面的代码,“原来的异常”向上传播调用堆栈,以及“最后例外”丢失。

1

异常传播开来,应该在更高级别处理。如果未在较高级别处理异常,则应用程序崩溃。 “finally”块的执行停止在抛出异常的地方。

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

  1. 如果说“最后”块被一个异常发生在try块,后执行

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

Great article for Details

相关问题