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

来源:http://www.cnblogs.com/vvjiang/archive/2016/04/05/5357519.html
-Advertisement-
Play Games

異常就是指成員沒有完成它的名稱所宣示的行動。 上面這段代碼會有異常,因為Troy去執行Love這個函數,然而其中girl根本就沒有賦值。本來Troy預期完成愛一個姑娘這個行動,結果發生了異常的事情,姑娘離開了Troy。 異常要解決的問題 很多行為(比如方法和屬性)很多時候都沒法返回錯誤代碼(比如vo ...


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

    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傳給泛型異常類來使用,以此起到簡化作用。

設計規範和最佳實踐

  • 不要什麼都捕捉
    • 就像前面說的捕捉異常表明已經預見到了此異常,理解它為什麼發生,並知道如何處理它。如果catch了System.Exception就表明你確定預知到了一切異常,並且知道如何處理,仿若神明。
    • 所以應該有針對性地捕捉異常,而不是吞噬異常,沒有捕捉到的異常請拋出。(有一種有趣的玩法就是用一個線程去吞噬異常然後給出結果,然後另一個線程去檢測結果然後重新拋出該異常)
  • 發生不可恢復的異常時回滾部分完成的操作——維護狀態
    • 捕捉到異常後看能否寫代碼簡單回滾,不行的話也可以用事務來處理。
  • 隱藏實現細節來維繫協定
    • 如果需要傳遞給上層更多的信息,可以直接在異常的Data屬性中添加信息
    • 可以嘗試著用對用戶而言更形象的異常去包裝實際發生的異常然後拋出,但是必須將實際發生的異常作為這個更形象的異常的InnerException。

未處理的異常

未處理的異常就是指那些未catch到的異常(調用棧向上查找也沒catch到)。

應用程式應建立處理未處理異常的策略,而微軟建議開發人員接收CLR的預設策略。也就是說,應用程式發生未處理異常時,程式終止,windows會向事件日誌寫一條記錄。

可通過事件查看器查看該記錄:

還可用可靠性監視程式查看應用程式的更多細節

圖上顯示我的dota2在3月25號又崩了,點後面可以查看詳細信息。

我們可以將未處理的異常自己去寫日誌記錄下來,或者發郵件什麼的都行。而微軟的每種應用程式模型都有自己的與未處理異常打交道的方式。

然而對於服務端程式而言,發生了未處理的異常,理想情況下是記錄日誌,然後向客戶端發送通知,表明請求無法完成,最終終止伺服器應用程式。(這個太扯了,作者也說太理想了)

對於伺服器應用程式,與未處理異常有關的信息不應該返回客戶端,首先用戶這些信息用戶並不能解決,其次伺服器應該儘量少暴露自己的相關信息,防止被黑。(這個必須保證)

對異常進行調試

異常處理的性能問題

約束執行區域

代碼協定


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 我們很高興宣佈2016年 V1 版本發佈了,可免費下載試用。 今年ComponentOne 將聚焦WinForm、WPF、MVC、UWP平臺和核心控制項Flex家族。 本次發佈主要包括UWP平臺;WinForm平臺的FlexPivot控制項和MVC平臺的FlexSheet控制項;FlexChart、Fle ...
  • 本文綜合整理自知乎同名問答帖,題主的補充: 比 如技能有:可以用cmd 命令查詢到電腦的各種狀態, 可以用快捷鍵瞬間轉換視窗頁面的軟體…當然這些都是些簡單的…還有神麽不為人知的高端技能大家可以來露一手麽? 有什麼網站或者論壇可以接觸到這些高端技能麽? 以前我覺得學電腦的室友可以再 cmd里設置電腦 ...
  • 發佈/訂閱 在之前的案例中我們創建了一個工作隊列,這個工作隊列的實現思想就是一個把每一個任務平均分配給每一個執行者,在這個篇文章我們會做一些不一樣的東西,把一個消息發送給多個消費者,這種模式就被稱作"發佈/訂閱". 為了說明這個模式,我們將要創建一個簡單的日誌系統,一個負責發佈消息,另外一個負責接收 ...
  • 分類:Unity、C#、VS2015 創建日期:2016-04-06 一、簡介 Unity自帶的資源包也稱為標準資源包。換言之,Unity自帶的所有標準資源包導入到Unity項目中以後,都會放在Project視圖的Standed Assets文件夾下。如果是多平臺,除了Standed Assets文... ...
  • 今天剛剛明白ref和out的區別,只限於個人理解如有不同請賜教,謝謝 首先我感覺ref和out是針對於值類型來說,以前一直認為是針對於引用類型看下麵的一段代碼 1.首先結果 i=0;ints[0]=0 i=0;ints[0]=100 2.ints作為一個引用類型在傳入方法後,對ints[0]進行了賦 ...
  • vs2005針對datatable已經有封裝好的去重覆方法: 1 //去掉重覆行 2 DataView dv = table.DefaultView; 3 table = dv.ToTable(true, new string[] { "name", "code" }); 4 5 此時table 就 ...
  • c# 正則表達式筆記 估計要寫幾天 看得一個不錯的正則教程包括字元串教程 C#字元串和正則表達式參考手冊.pdf 正則所需要的命名空間是 using System.Text.RegularExpressions 它包含了8個類,用得最多是的Regex; Regex不僅可以用來創建正則表達式,而且提供 ...
  • 下圖👇是我在CodeL公眾號菜單上設置的特殊符號,你也可以關註CodeL公眾號查看。 網頁打開圖標可能是黑白的 ,微信查看是彩色的。 是不是很漂亮 🚶 🚼 🙌 👈看,不只是菜單,內容裡面也可以加入這些特殊符號,接下來我們就來看看是如何實現的 下麵這張特殊符號的表來源:土錘哥 有了這份寶典, ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...