異常就是指成員沒有完成它的名稱所宣示的行動。
public class Girl { public string Name { get; set; } } public class Troy{ Girl girl; public void Love() { Console.WriteLine("Troy愛上了" + girl.Name); } }
上面這段代碼會有異常,因為Troy去執行Love這個函數,然而其中girl根本就沒有賦值。本來Troy預期完成愛一個姑娘這個行動,結果發生了異常的事情,姑娘離開了Troy。
異常要解決的問題
很多行為(比如方法和屬性)很多時候都沒法返回錯誤代碼(比如void方法,構造器,屬性的獲取設置),但他們仍然需要報告錯誤,於是異常就來解決這個問題。
也就是說異常處理機制實際上是為了返回可預知的錯誤代碼,而不是為了去捕獲未知的異常讓程序不報錯。(這一點非常重要)
不要去讓程序吞異常,不把異常暴露出來讓其繼續運行,反而可能使程序做出更錯誤的舉動。(有錯就改,別藏著)
那麼其實我在剛學習的時候一直有個疑問,我這個系統很多人在用啊,你如果不吞異常,那報黃頁不是更6?
現在我認為這並不矛盾,如果有異常就在catch後進行異常處理還原操作,然後寫日志或者用一個統一的頁面去提示用戶出錯了,而不是把黃頁去給用戶看。(就像你告訴別人你得胃病了,用嘴和肢體語言表述都行,你剖開自己的肚子告訴別人你有病就是你的錯了啊)
.Net的異常處理機制
.Net的異常處理機制是基於windows提供的結構化異常處理機制(Structured Exception Handing,簡稱SEH)構建的。
異常處理的代碼就不演示了,說說三大塊
static void Main(string[] args) { try { FuncA(); } finally { Console.WriteLine("主函數Finally"); } Console.Read(); } static void FuncA() { try { Object obj = new DateTime(); int a = (int)obj;//這裡會報System.InvalidCastException異常 } catch(InvalidDataException)//表示不匹配,然後到調用棧的上一級也就是main函數,然而main函數中的try根本就沒有catch所以更談不上什麼匹配,也就是出現了一個未處理的異常 { //這裡完全不會執行 } finally {//雖然有Finally說好的,不論是否異常都會執行,然而此時上面的異常沒有catch到,
//所以已經異常報錯了,不會再執行到這裡。此時CLR會終止進程,相較於讓程序繼續運行造成不可預知的結果這樣更好 Console.WriteLine("函數A的Finally"); } }
System.Exception類
微軟規定所有CLS相容的編程語言都必須拋出和捕捉派生自該類型的異常。
一般來講也就這個類中也就三個屬性要注意:
拋出異常
拋異常需要考慮兩個問題:
第一個是拋出什麼Exception類型的異常。應該選擇一個更有意義的類型。要考慮到調用棧中高處的代碼,要知道那些代碼如何判斷一個方法失敗從而執行得體的恢復代碼。作者強烈建議異常的繼承層次結構應該淺而寬,這樣就可以盡量少的創建基類。而基類意味著把眾多錯誤當做一個錯誤來處理。
第二個是向異常類型的構造器傳遞什麼字符串消息。
自定義異常類
看起來自定義異常類很簡單,只需要繼承System.Exception類就OK了,然而實際上這是個很繁瑣的事情。
因為從System.Exception類派生出來的所有類都應該是可序列化的,使它們能穿越AppDomain邊界去寫入日志或者數據庫。而序列化就涉及到很多問題。
作者寫了個泛型異常類去簡化,我這裡就不寫了,實際上在格式上找個系統異常照著寫就行了:
別忘了在自定義類上面加上[Serializable]特性。
作者的玩法更高端一點,自己建個泛型異常類繼承Exception,然後將一些構造函數或者序列化函數寫在這個類中。個性化的異常信息作為泛型變量T傳給泛型異常類來使用,以此起到簡化作用。
------未完待續,以上講得還是些基礎,接下來才會寫如何去正確得使用異常處理機制---------
用可靠性換取開發效率
設計規范和最佳實踐
未處理的異常
對異常進行調試
異常處理的性能問題
約束執行區域
代碼協定