2010-05-26 163 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

相關問題