[.NET] 例外處理原則

例外處理 (Exception Handling) 是每個寫程式的人都會遇到的問題,其實處理例外就像呼吸那麼自然,而例外處理的工作不外乎...

例外處理 (Exception Handling) 是每個寫程式的人都會遇到的問題,其實處理例外就像呼吸那麼自然,而例外處理的工作不外乎:

  1. 給予開發人員足夠的情報,以偵測或檢查問題,這會關乎於除錯的速度。
  2. 給予使用者適當的回饋,以讓使用者有辦法處理或執行不同的動作以規避例外狀況。
  3. 給予稽核人員足夠的資訊,以確保系統的穩定度。
  4. 給予客戶品質的保證,以確認引發例外的不是系統而是人因。

而在 C# 中,最常見的莫過於擲出例外 (throw exception),例外擲出後如果中間的程式都沒有攔截它做任何處理的話,那麼它最終就會回到使用者介面上,也就是使用者會看到莫名奇妙的訊息,例如什麼 … “並未將物件參考設定為物件的執行個體”或是 “索引超出集合的範圍” 或是 “參數錯誤”等等。一般來說,寫在程式內的例外都很文言文的,這也是 Framework 經常設計的方式,它給予了使用 Framework 的開發人員解釋這些例外的權利,當然也等於是要開發人員負自行去實作程式以解釋這些例外的責任,所以現在例外基本上都要開發人員處理,以回饋或達到前面所說四個工作的要求。

當然啦,開發人員擁有了解釋例外的權利,就會有一些奇奇怪怪的解釋方式,像是:

  1. 什麼都不做,然後讓 User 來罵。
  2. 攔截了例外,但是放過了未預期的例外。
  3. 攔截了例外,卻沒有經過包裝就擲回去。
  4. 攔截了例外,也有經過適當的處理。

在一個有要求的開發團隊內,第一種是絕對不允許的,雖然可能會有漏網之魚,但基本上要求這類型的不能存在,第二種則是開發人員還不夠嚴謹,第三種爭論較多,有些人認為重擲會影響效能,這點也沒錯,但必要的包裝我認為還是需要的,尤其是利用 Framework 開發新 Framework 的開發人員。第四種則是比較好的作法。本站昏睡名人Clark也發表過他對錯誤處理的看法

我個人認為,錯誤處理 (error handling) 要做的應該是適當的處理可預期的錯誤,而未預期的錯誤則是要經過包裝,再告訴使用者聯絡程式開發人員或是團隊來處理 (做得更好的團隊也許會內植一個自動化的 Bug Report 來回報這種未預期的錯誤)。而通常 try/catch 會作為錯誤處理的機制,當然也可以再加上一些錯誤處理的模組來做,整個錯誤處理的過程如下圖:

 

image

 

在 MSDN 中,也有一些說明與方針來建議開發人員適度的處理錯誤:

  1. 請勿在架構程式碼中藉由攔截類似System.Exception、System.SystemException 等非特定的例外狀況來處理錯誤。
  2. 避免在應用程式的程式碼中藉由攔截類似System.Exception、System.SystemException 等非特定的例外狀況來處理錯誤。也會發生可以接受處理應用程式中錯誤的情況,但是這類情況很罕見。
  3. 當攔截傳輸例外狀況的目的時,請不要排除任何特殊的例外狀況。
  4. 當您了解給定的內容中為何將擲回特定例外狀況時,可以考慮攔截這些例外狀況。
  5. 請勿過度使用攔截,應該經常允許例外狀況散佈到呼叫堆疊上。
  6. 請使用 try-finally 並避免使用 try-catch 來清除程式碼。在編寫完善的例外狀況程式碼中,try-finally 遠比 try-catch 更為常用。
  7. 當攔截並重新擲回例外狀況時,最好使用空白擲回方式,因為這是保留例外狀況呼叫堆疊的最好方式。
  8. 請不要使用無參數的 catch 區塊來處理不符合 CLS 標準的例外狀況 (不是衍生自 System.Exception 的例外狀況)。可支援不是衍生自 Exception 的例外狀況之語言可以自由處理那些不符合 CLS 標準的例外狀況。

其實,只要開發人員能把握幾個原則,就能妥善的處理好程式中的例外:

  1. 如果例外可以預期,那麼只要處理預期的例外即可,未預期的例外則讓它繼續往上擲 (即在 catch 中使用 throw 將它擲上去即可,不要使用 throw new 或將 catch 的例外變數擲回,那會讓原始擲出的堆疊資料消失)。
  2. 針對未預期的例外,可透過全域型的方式 (例如 Application.UnhandledException 事件常式,或是 ASP.NET 中 Global.asax 內的 Application_Error 事件常式) 攔截並處理。
  3. 回報給開發人員的,盡量以縮小偵錯範圍為原則,並包裝必要的 Stack Trace 內容。
  4. 回報給使用者的,請使用具親和力的訊息內容,並指示使用者可用的下一步動作為何。
  5. 錯誤記錄應盡可能詳細,以縮短找出錯誤位置的時間,例如傳入參數內容,類型等,或是更進一步的記錄變數的內容等。

如果想要設計集中的錯誤處理機制,那麼可以考慮使用 Microsoft Patterns and Practices 提供的 Enterprise Library 內的 Exception Handling Application Block