C#筆記 – 異常處理
-
異常
- 成員沒有完成它的名稱所宣稱的行動
-
C#中的異常處理使用
-
public static void ExceptionControl() { string a = null; try { a.Contains("Str"); } catch (NullReferenceException e) { Console.WriteLine("Null Reference Exception Caught!"); } catch { Console.WriteLine("Some Exception Caught"); } finally { Console.WriteLine("Exception Test Finished"); } } -
try塊
-
應包含以下類型的代碼
-
需要執行的資源清理操作的代碼
-
需要從異常中恢復的代碼
-
可能會拋出異常的代碼
-
-
一個try塊至少要有一個關聯的catch塊/finally塊
-
-
catch塊
-
響應一個異常需要執行的代碼
-
可以有0個或多個catch塊
-
如果try塊代碼沒有造成異常的拋出,則永遠不會執行任何catch塊,直接執行finally塊(如有)
-
catch塊後的圓括號中的表達式為「捕捉類型」
-
該類型必須派生自System.Exception
-
如果只有catch關鍵字,沒有指定任何捕捉類型,代表捕捉除了上面catch塊指定了的具體捕捉類型以外的捕捉類型
- 如果在捕捉類型後指定一個變量,該變量將引用拋出的System.Exception派生對象
-
CLR自上而下搜索匹配的catch塊,因此越是具體的(從System.Exception派生得越遠)的異常,應最先被捕捉
-
如果搜索過後,沒有任何捕捉類型與拋出的異常匹配,CLR會去調用棧更高的一層嘗試搜索匹配的異常捕捉類型
-
一旦找到,就會執行「內層」的所有finally塊代碼
-
-
-
-
finally塊
-
保證會執行
-
一般執行try塊行動所要求的資源清理操作
-
public static void ReadData(string path) { FileStream fs = null; try { fs = new FileStream(path, FileMode.Open); } catch (IOException) { } finally { if(fs != null) { fs.Close(); } } } -
如在該段代碼中,無論try中的代碼有沒有拋出異常,文件保證會被finally中的代碼所關閉
-
否則,如果文件流關閉的代碼放了在finally塊之後的地方,那當try塊拋出異常後,finally塊後的語句沒法被執行,文件流也就無法被關閉
-
因此,C#編譯器看到一些必須進行清理的代碼,會自動生成try/finally塊
- lock/using/foreach/定義析構器
-
-
一個try塊最多只能關聯一個finally塊
-
-
System.Exception
-
雖然CLR允許異常拋出任何類型的實例,但Microsoft定義了System.Exception類,並規定所有CLS相容的語言都必須能拋出和捕捉派生自該類型的異常
-
派生自System.Exception的異常類型被認為是CLS相容的
-
C#也只允許拋出CLS相容的異常
-
-
System.Exception提供了一個唯讀的StackTrace屬性
-
catch塊可讀取該屬性來獲取一個stack trace,它描述了異常發生前調用了哪些方法
-
訪問該屬性會調用CLR的代碼
- 一個異常拋出時,CLR在內部記錄throw指令的位置
-
約束執行區域(CER)
-
CER是必須對錯誤有適應力的代碼塊
- 在拋出了非預期的異常時維護狀態非常有用
-
RuntimeHelpers.PrepareConstrainedRegions
-
public static void Demo() { RuntimeHelpers.PrepareConstrainedRegions(); try { Console.WriteLine("In Try"); } finally { Type2.M(); } } public class Type2 { static Type2() { Console.WriteLine("Type2 ctor called"); } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] public static void M() { } } -
JIT編譯器如果發現在一個try塊前調用了RuntimeHelpers.PrepareConstrainRegions,就會提前編譯與try關聯的catch和finally塊中的代碼
- 但是,在catch/finally塊裡被調用的方法,必須應用ReliabilityContract特性。且傳遞了Consistency.WillNotCorruptState/Consistency.MayCorruptInstance枚舉值
-
代碼協定
-
代碼協定提供了直接在代碼中聲明代碼設計決策的一種方式
-
前條件:驗證實參
-
後條件:方法因返回/拋出異常而終止時,對狀態進行驗證
-
對象不變性:在對象生命期內確保對象的字段的良好狀態
-
-
核心為靜態類:System.Diagnostics.Contracts.Contract
參考書目
- 《CLR via C#》(第4版) Jeffrey Richter