.NET異常編程模型漫談
此篇博文只是對.NET異常編程模型隨便聊聊不會系統性的講。異常編程模型這個名字叫的較屌,但是這個編程模型卻很親民,.NET異常編程模型就是在callee(被調用方法)中拋出異常,在caller(調用方法的方法)中捕獲異常。從C語言到Java,再到C#異常模型比較統一,大家也經常去寫try...catch...。
ApplicationException(AE)和SystemException(SE)
最初.NET團隊設計AE和SE的目的是為了區分應用程序異常和BCL異常,繼承自AE的自定義異常在應用程序代碼中拋出,繼承自SE的異常在BCL的代碼段中拋出。
這樣可以友好的將異常分為兩類,并且根據其基類就可以判斷這個異常在哪里拋出,但是目前有這樣的兩個問題。
1.ArgumentException等繼承自SE,但是ArgumentException是一個很通用的異常類,真心沒有必要再在應用程序中定義一遍。
2.TargetInvocationException 是定義在BCL中的類,但是他繼承自AE。
從1和2看目前將異常分為AE和SE兩類的原則就不再適用了。當然BCL中提供的AE和SE不適用也沒關系,如果真的有必要區分應用程序異常和.NET BCL異常的話我們可以自定義一個異常基類。讓所有的應用程序自定義異常都去繼承這個基類。但是真的有必要嗎?我們在caller捕獲的異常大多會預測到或者是大多是由callee在注釋中提供。我們想去特殊處理哪種異常都在代碼中捕獲,不想做特殊處理的異常則直接用Exception捕獲即可。這樣看來在注釋中出現我們自定義的異常基類就會顯的很雞肋,用張全蛋的一句話來說,其實這樣定義一個異常基類并沒什么卵用。
吐槽一下.NET異常編程模型的設計
C#.NET出現的比java晚一些,并且有參考java做設計,從我的角度來說,我并不完全滿意.NET團隊提供異常模型,首先是BCL中提供的AE和SE,這樣做只會加深了異常的依賴深度。其次,與java的異常編程模型相比,我更喜歡Java的異常編程模型。在java中要么就向上層繼續拋出異常,在方法簽名中寫明,要么捕獲異常,如果不這樣做就會出現編譯錯誤,而在C#中開發人員可以忽略callee中拋出的異常,我認為在代碼中捕獲所有異常,寫異常處理邏輯是非常必要的。
善用異常編程模型原則
異常編程模型實現了編程語言的錯誤報告機制,callee主動拋出異常,捕獲異常caller捕獲異常,中間可能會有幾次甚至幾十次調用發生,但是中間的caller并沒有異常傳播代碼。所以我們應該善用異常編程模型去構建健壯,易于維護的應用程序。以下是我的對使用異常編程模型的六個建議
- 在代碼主動拋出異常標識處理失敗,而不是通過返回Bool值或者ref out等傳入的參數標識處理成功失敗
- 熟悉目前.NET平臺BCL提供的異常類。
- 在BCL中找不到使用的異常類,則在應用程序中添加自定義異常,個人建議定義在Model中,便于異常的傳播。
- 若非業務需要則統一捕獲異常,一般都會在原有簡單的業務的復雜度有一定的成長性,原本業務邏輯代碼就夠多的了,異常處理邏輯就不要添亂了,把異常處理邏輯拿出來放在Facade層。可以實現業務邏輯和異常處理邏輯解耦的目的。
- 在方法頭部注釋中標識出會拋出的異常,若caller未對callee捕獲異常也要將callee中的異常注釋復制到caller的頭部的注釋中。
- 自定義異常直接從Exception繼承就好,完全不用鳥ApplicationException。