程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 【C#進階系列】19 異常和狀態管理,

【C#進階系列】19 異常和狀態管理,

編輯:C#入門知識

【C#進階系列】19 異常和狀態管理,


異常就是指成員沒有完成它的名稱所宣示的行動。

    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)構建的。

異常處理的代碼就不演示了,說說三大塊

  • try塊
    • 一個try塊中如果能拋出同一個異常類的操作,卻要進行不同的異常恢復措施,那麼應該分成兩個try塊。
    • try和finally到一起一般是執行資源清理操作(也可以用using哦)。
  • catch塊
    • 一個try塊可以關聯0個或多個catch塊。
    • catch後面跟著圓括號中的表達式稱為捕捉類型,異常捕捉類型必須是System.Exception或者它的派生類。
    • CLR自上而下搜索異常,所以要將較具體的異常放在頂部。也就是說首先寫派生程度最大的異常,然後才是其基類,然後才是System.Exception或者不指定任何捕捉類型的catch塊。
    • 如果拋出的異常沒有catch到,也就是說catch的類型沒有一個與拋出的異常匹配,那麼CLR就回去調用棧更高的一層搜索與異常匹配的捕捉類型。如果到了調用棧的頂部還是沒有匹配到catch塊,就會發生未處理的異常。而一旦找到匹配的catch塊,就會執行內層所有finally塊的代碼,否則內層所有finally塊的代碼都不會執行。也就是說下面示例代碼中會報異常:
             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"); } }
    • catch塊的末尾有以下三種處理方法:
      • 重新拋出相同的異常,向調用棧高一層的代碼通知該異常的發生,也就是throw;
      • 拋出一個不同的異常,向調用棧高一層的代碼提供更豐富的異常信息,也就是throw ex;//這裡ex為新的異常對象
      • 讓線程從catch塊底部退出,不向更高層拋異常。
    • 代碼可向AppDomain的FirstChanceException事件登記,這樣只要AppDomain一發生異常就會收到通知,並且在CLR開始搜索任何catch塊之前就會調用這些事件回調函數。
  • finally塊
    • finally塊為保證會執行的代碼。
    • 如果在catch內部和finally內部又拋出了異常,那麼在try中的異常不會被記錄,其信息將丟失。

System.Exception類

微軟規定所有CLS相容的編程語言都必須拋出和捕捉派生自該類型的異常。

一般來講也就這個類中也就三個屬性要注意:

  • Message指出拋出異常的原因
  • InnerException如果當前異常是在處理一個異常時拋出的,那麼InnerException中就是上一個異常。用公共方法GetBaseException可以遍歷內部異常鏈表,返回最初拋出的異常。
  • StackTrace包含異常拋出前調用過的所有方法的名稱和簽名。它返回一個從異常拋出位置到異常捕捉未知的所有方法。

拋出異常

拋異常需要考慮兩個問題:

第一個是拋出什麼Exception類型的異常。應該選擇一個更有意義的類型。要考慮到調用棧中高處的代碼,要知道那些代碼如何判斷一個方法失敗從而執行得體的恢復代碼。作者強烈建議異常的繼承層次結構應該淺而寬,這樣就可以盡量少的創建基類。而基類意味著把眾多錯誤當做一個錯誤來處理。

第二個是向異常類型的構造器傳遞什麼字符串消息。

自定義異常類

看起來自定義異常類很簡單,只需要繼承System.Exception類就OK了,然而實際上這是個很繁瑣的事情。

因為從System.Exception類派生出來的所有類都應該是可序列化的,使它們能穿越AppDomain邊界去寫入日志或者數據庫。而序列化就涉及到很多問題。

作者寫了個泛型異常類去簡化,我這裡就不寫了,實際上在格式上找個系統異常照著寫就行了:

別忘了在自定義類上面加上[Serializable]特性。

作者的玩法更高端一點,自己建個泛型異常類繼承Exception,然後將一些構造函數或者序列化函數寫在這個類中。個性化的異常信息作為泛型變量T傳給泛型異常類來使用,以此起到簡化作用。

------未完待續,以上講得還是些基礎,接下來才會寫如何去正確得使用異常處理機制---------

用可靠性換取開發效率

設計規范和最佳實踐

未處理的異常

對異常進行調試

異常處理的性能問題

約束執行區域

代碼協定

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved