簡體   English   中英

C#:拋出自定義異常最佳實踐

[英]C#: Throwing Custom Exception Best Practices

我已經閱讀了一些關於C#異常處理實踐的其他問題,但似乎沒有人問我在尋找什么。

如果我為特定的類或一組類實現我自己的自定義Exception。 是否應該使用內部異常將與這些類相關的所有錯誤封裝到我的異常中,還是應該讓它們通過?

我認為捕獲所有異常會更好,以便可以從我的源代碼中立即識別異常。 我仍然將原始異常作為內部異常傳遞。 另一方面,我認為重新拋出異常是多余的。

例外:

class FooException : Exception
{
    //...
}

選項1:Foo包圍所有例外:

class Foo
{
    DoSomething(int param)
    {
        try 
        {
             if (/*Something Bad*/)
             {  
                 //violates business logic etc... 
                 throw new FooException("Reason...");
             }
             //... 
             //something that might throw an exception
        }
        catch (FooException ex)
        {
             throw;
        }
        catch (Exception ex)
        {
             throw new FooException("Inner Exception", ex);
        }
    }
}

選項2:Foo拋出特定的FooExceptions但允許其他異常落空:

class Foo
{
    DoSomething(int param)
    {
        if  (/*Something Bad*/)
        {
             //violates business logic etc... 
             throw new FooException("Reason...");
        }
        //... 
        //something that might throw an exception and not caught
    }
}

根據我對庫的經驗,您應該在FooException包裝所有內容(您可以預期),原因如下:

  1. 人們知道它來自你的課程,或者至少來自他們的使用。 如果他們看到FileNotFoundException他們可能正在尋找它。 你正在幫助他們縮小范圍。 (我現在意識到堆棧跟蹤就是為了這個目的,所以也許你可以忽略這一點。)

  2. 您可以提供更多上下文。 用您自己的例外包裝FNF,您可以說“我試圖為此目的加載此文件,但找不到它。這暗示了可能的正確解決方案。

  3. 您的庫可以正確處理清理。 如果你讓異常氣泡,你就強迫用戶清理。 如果你已經正確地封裝了你正在做的事情,那么他們就不知道如何處理這種情況!

請記住只包裝您可以預期的異常,例如FileNotFound 不要只包裝Exception並希望最好。

看看這個MSDN最佳實踐

如果你想重新拋出捕獲的異常,請考慮使用throw而不是throw ex ,因為這樣原始的堆棧跟蹤會保留(行號等)。

在創建自定義異常時,我總是添加一些屬性。 一個是用戶名或ID。 我添加了一個DisplayMessage屬性來攜帶要顯示給用戶的文本。 然后,我使用Message屬性來傳達要記錄在日志中的技術細節。

我捕獲數據訪問層中的每個錯誤,我仍然可以捕獲存儲過程的名稱和傳遞的參數值。 或內聯SQL。 也許是數據庫名稱或部分連接字符串(請不要憑據)。 這些可以在Message或他們自己的新自定義DatabaseInfo屬性中。

對於網頁,我使用相同的自定義異常。 我將在Message屬性中輸入表單信息 - 用戶在網頁上的每個數據輸入控件中輸入的內容,正在編輯的項目的ID(客戶,產品,員工,等等)以及用戶的操作異常發生時正在服用。

所以,根據你的問題,我的策略是:只有在我可以對異常做些什么時才會抓住。 通常,我所能做的就是記錄細節。 所以,我只抓住那些細節可用的點,然后重新拋出讓異常泡到UI。 我在自定義異常中保留了原始異常。

自定義異常的目的是為堆棧跟蹤提供詳細的上下文信息以幫助調試。 選項1更好,因為沒有它,如果它在堆棧中出現“較低”,則不會獲得異常的“起源”。

如果在Visual Studio中運行“異常”的代碼片段,則會有一個良好練習異常編寫的模板。

注意選項1: throw new FooException("Reason..."); 不會被捕獲,因為它在try / catch塊之外

  1. 您應該只捕獲要處理的異常。
  2. 如果你沒有向異常添加任何額外數據而不是使用throw; 因為它不會殺死你的堆棧。 在選項2中,你仍然可以在catch中進行一些處理並且只是調用throw; 用原始堆棧重新拋出原始異常。

對於代碼來說,最重要的是要知道何時捕獲異常(不幸的是,異常對象完全丟失)是系統相對於“應該”的狀態(可能是由於出現了錯誤而拋出了異常)。 如果LoadDocument方法中發生錯誤,可能是文檔未成功加載,但至少有兩種可能的系統狀態:

  1. 系統狀態可能好像從未嘗試過負載。 在這種情況下,如果應用程序在沒有加載文檔的情況下可以繼續,那么應用程序將繼續完全正確。
  2. 系統狀態可能已經充分損壞,最佳操作方法是保存可以保存到“恢復”文件的內容(避免用可能損壞的數據替換用戶的好文件)並關閉。

顯然,在這些極端之間經常會有其他可能的狀態。 我建議人們應該努力擁有一個自定義異常,明確指出狀態#1存在,如果可預見但可能會導致#2,可能會導致#2。 發生並將導致狀態#1的任何異常都應該包含在指示狀態#1的異常對象中。 如果異常可能以系統狀態可能受到損害的方式發生,則它們應該被包裝為#2或允許滲透。

選項2是最好的。 我認為最佳做法是僅在您計划對異常執行某些操作時捕獲異常。

在這種情況下,選項1只是用您自己的異常包裝異常。 它沒有添加任何值,並且您的類的用戶不能再只捕獲ArgumentException,例如,他們還需要捕獲您的FooException然后解析內部異常。 如果內部異常不是例外,他們可以做一些有用的事情,他們需要重新拋出。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM