2008-09-22 150 views
33

我已經看到下面的代碼很多次:爲什麼重新拋出異常?

try 
{ 
    ... // some code 
} 
catch (Exception ex) 
{ 
    ... // Do something 
    throw new CustomException(ex); 

    // or 
    // throw; 

    // or 
    // throw ex; 
} 

能否請您解釋重新拋出異常的目的是什麼?它遵循異常處理中的模式/最佳做法嗎? (我讀過的地方叫做「呼叫者通知」模式?)

回答

38

如果您想記錄異常但不處理它,則重試相同的異常很有用。

拋出一個包含捕獲的異常的新異常對抽象很有用。例如,您的圖書館使用第三方庫,該庫引發您的圖書館的客戶端不應該知道的異常。在這種情況下,你可以將它包裝成更爲本地化的異常類型,然後拋出它。

+0

我理解是否正確 - 沒有必要處理重新拋出的異常? – chester89 2008-11-26 11:03:51

+4

我的意思是這樣的:如果你有一段代碼無法處理異常(即,你希望你的調用者來處理它),你有兩個選擇:1.不要抓住它; 2.抓住它,記錄在某個地方,然後重新拋出它。通過重新推出,呼叫者看起來好像根本沒有被發現。 – 2008-11-27 05:04:22

0

直到我開始使用EntLib ExceptionBlock,我在使用EntLib ExceptionBlock之前先使用它們來記錄錯誤,然後再拋出它們。當你認爲我可以在那個時候處理它們的時候有點討厭,但是當時最好讓它們在UAT中(在記錄它們之後)快速地失敗,而不是覆蓋流動的錯誤。

0

該應用程序很可能捕捉到調用堆棧中較高的重新拋出的異常,因此重新拋出它們允許較高的處理程序截獲並適當地處理它們。應用程序擁有一個記錄或報告期望的頂級異常處理程序是很常見的。

另一種選擇是,編碼器是懶惰的,而不是捕捉他們想要處理的一組異常,他們抓住了一切,然後重新拋出只有那些他們實際上無法處理的異常。

1

通常,「做某事」或者涉及更好地解釋異常(例如,將其封裝在另一個異常中)或通過某個源跟蹤信息。

另一種可能性是,如果異常類型沒有足夠的信息來知道是否需要捕獲異常,在這種情況下捕獲它以檢查它將提供更多信息。

這並不是說這種方法純粹是出於好的理由而使用的,它多次用於開發人員認爲在未來某個時候可能需要跟蹤信息,在這種情況下,您可以嘗試{} catch {throw;}風格,這根本沒有幫助。

1

我認爲這取決於你對異常做什麼。

一個很好的理由是首先在錯誤中記錄錯誤,然後將錯誤提交給UI以生成一個友好的錯誤消息,並且可以選擇查看錯誤的更多「高級/詳細」視圖,包含原始錯誤。

另一種方法是「重試」方法,例如保留一個錯誤計數,並在經過一定次數的重試之後,這是唯一一次將錯誤發送到堆棧的時間(有時這是爲數據庫調用的數據庫訪問完成的該超時或通過慢速網絡訪問網絡服務)。

雖然會有一堆其他的原因。

-3

重拋異常的主要原因是保持調用堆棧不變,以便您可以更全面地瞭解發生的情況並調用序列。

8

根據代碼在應用程序體系結構中的位置,您通常會捕獲並重新拋出兩個原因之一。

在一個應用程序的核心,你通常會捕獲並重新拋出異常,將其轉化爲更有意義的異常。例如,如果您正在編寫數據訪問層並在SQL Server中使用自定義錯誤代碼,則可能會將SqlException轉換爲像ObjectNotFoundException之類的內容。這很有用,因爲(a)它使得調用者更容易處理特定類型的異常,(b)因爲它阻止了該層的實現細節,例如使用SQL Server進行持久性泄露到其他層的事實使您可以更輕鬆地改變未來的事物。

在應用程序的邊界處,通常會捕獲並重新拋出而不翻譯​​異常,以便記錄其詳細信息,幫助調試和診斷實時問題。理想情況下,您希望在操作團隊可以輕鬆監控的地方(例如事件日誌)發佈錯誤,並在某處爲開發人員(通常是跟蹤)提供控制流中發生異常的上下文。

24

實際上有

throw new CustomException(ex); 

throw; 

第二之間的差將保留堆棧信息。

但有時候你想讓異常對你的應用程序域更「友好」,而不是讓DatabaseException到達你的GUI,你會引發包含原始異常的自定義異常。

例如:

try 
{ 

} 
catch (SqlException ex) 
{ 
    switch (ex.Number) { 
     case 17: 
     case 4060: 
     case 18456: 
      throw new InvalidDatabaseConnectionException("The database does not exists or cannot be reached using the supplied connection settings.", ex); 
     case 547: 
      throw new CouldNotDeleteException("There is a another object still using this object, therefore it cannot be deleted.", ex); 
     default: 
      throw new UnexpectedDatabaseErrorException("There was an unexpected error from the database.", ex); 
    } 
} 
2

我能想到的原因如下:

  • 保持設定的固定拋出的異常類型,作爲API的一部分,使主叫方只擔心固定的例外情況。在Java中,由於檢查的異常機制,您實際上被迫這樣做。

  • 向異常添加一些上下文信息。例如,您可能想要捕獲它,並在處理訂單號XXX,查找產品YYY時添加「...」,而不是讓裸露的「未找到記錄」。

  • 做一些清理 - 關閉文件,回滾事務,釋放一些句柄。

10

有時候,你想隱藏的方法的實施細則或改善 問題的抽象級別,以便它更有意義的方法的調用者 。爲此,您可以攔截原始異常,並用 替代更適合於解釋問題的自定義異常。

以一個從文本文件加載所請求的用戶詳細信息的方法爲例。該方法假設存在一個以用戶ID和後綴「.data」命名的文本文件。當該文件實際上不存在時,拋出FileNotFoundException沒有多大意義,因爲每個用戶的詳細信息都存儲在文本文件中的事實是該方法內部的實現細節。所以這個方法可以用一個解釋性的消息將原始的異常封裝在一個自定義異常中。

與顯示的代碼不同,最佳做法是通過將原始異常作爲新異常的InnerException屬性加載來保留原始異常。這意味着如果有必要,開發人員仍然可以分析潛在的問題。

當你創建一個自定義異常,這裏是一個有用的清單:

•查找傳達爲什麼異常被拋出,並確保該名稱以單詞「異常」結尾的好名字。

•確保您實現了三個標準異常構造函數。

•確保使用Serializable屬性標記異常。

•確保您實現了反序列化構造函數。

•添加任何可能幫助開發人員更好地理解和處理異常的自定義異常屬性。

•如果您添加任何自定義屬性,請確保您實現並重寫GetObjectData以序列化您的自定義屬性。

•如果添加任何自定義屬性,請重寫Message屬性,以便您可以將屬性添加到標準異常消息。

•請記住使用自定義異常的InnerException屬性附加原始異常。

0

如果你看一下例外作爲另一種方式來獲得的方法結果,然後重新拋出異常是喜歡你的包裹將導致一些其他的對象。

這是一個非常規世界中的常見操作。通常這發生在兩個應用程序層的邊界上 - 當來自圖層B的函數調用層C中的函數時,它將C的結果轉換爲B的內部窗體。

A -- calls --> B -- calls --> C

如果沒有,那麼在它調用層BA會有全套JDK例外處理。 :-)

正如接受的答案指出的那樣,層A甚至可能不知道C的例外。

層A,小服務程序:檢索一個圖像,它的元信息
層B,JPEG庫:收集可用DCIM標籤解析JPEG文件
層C ,一個簡單的DB:一個類讀取字符串記錄隨機訪問文件。有些字節被破壞,所以它會拋出一個異常,說「無法讀取記錄的'UTF-8字符串'bibliographicCitation'」。

所以A不會理解'書目引用'的含義。因此B應將此例外A翻譯爲TagsReadingException,其中包含原件。

相關問題