異常和狀態管理1
異常是指成員沒有完成它的名稱所宣稱的行動。
如 FileStream 的 方法裏有 Read,Write,等等(行動成員通常用動詞表示)。當行動成員不能完成任務時,就應拋出異常。
try:
如果代碼需要執行一般性的資源清理操作,需要從異常中恢復,或者兩者都需要,就可以放到 try 塊中。負責清理的代碼應該放到一個 finally 塊中。try 塊還可包含也許會拋出異常的代碼。負責異常恢復的代碼應放到一個或多個 catch 塊中。針對應用程序能從中安全恢復的每一種異常,都應該創建一個 catch 塊。一個 try 塊至少要有一個關聯的 catch 塊或 finally 塊,單獨一個 try 塊沒有意義,C# 也不允許。
開發人員有時不知道應該在一個 try 塊中放入多少代碼。這具體取決於狀態管理。如果在一個 try 塊中執行多個可能拋出同一個異常類型的操作,但不同的操作有不同的異常恢復措施,就應該將每個操作都放到它自己的 try 塊中,這樣才能正確地恢復狀態。
catch:
catch 塊包含的是響應一個異常需要執行的代碼。一個 try 塊 可以關聯0個或多個 catch 塊。如果try 塊中的代碼沒有造成異常的拋出,CLR 永遠不會執行它的 catch塊。線程將跳過所有 catch 塊,直接直觀性 finally 塊(如果有的話)。
catch 關鍵字後的圓括號中的表達式稱為捕捉類型。c# 要求捕捉類型必須是 System.Exception 或者它的派生類型。例如處理 InvalidOperationException 異常和 IOException 的 catch 塊。最後一個 catch 塊沒有指定捕捉類型,能處理除了前面的 catch塊指定的之外的其他所有異常;這相當於捕捉 System.Exception。
CLR 自上而下搜索匹配的 catch 塊,所以應該將較具體的異常放在頂部。也就是說,首先出現的是派生程度最大的異常類型,接著是它們的基類型(如果有的話),最後是System.Exception。
在 try 塊的代碼(或者從 try 塊調用的任何方法)中拋出異常,CLR 將搜索捕捉類型與拋出的異常相同(或者是它的基類)的 catch 塊。如果沒有任何捕捉類型與拋出的異常匹配,CLR會去調用棧更高的一層搜索與異常匹配的捕捉類型。如果都到了調用棧的頂部,還是沒有找到匹配的 catch 塊就會發生未處理的異常。一旦 CRL 找到匹配的 catch 塊,就會執行“內層”所有的 finally 塊中的代碼。所謂“內層 finally 塊” 是指從拋出異常的 try 塊開始,到匹配異常的 catch 塊之間所有的 finally 塊。註意,匹配異常的那個 catch 塊所關聯的 finally 塊尚未執行,該 finally 塊中的代碼一直要等到這個 catch 塊中的代碼執行完畢才執行。
static void Main(string[] args) { /* 嵌套try塊 * try * { * //A * try * { * //B * } * catch * { * //C * } * finally * { * //D * } * //E * } * catch * { ... } * finally * { ... } * * 拋出異常在:內層A,E處由外層catch塊捕獲,並執行外層finally * 拋出異常在:內層B處,且有一合適內層catch捕獲,執行內層finally,後執行E處 * 拋出異常在:內層B處,但內層catch塊沒有合適處理程序,執行內層finally,搜索外層catch,找合適的,執行外層finally,此時不會執行E * 拋出異常在:內層C處,退出內層catch塊,執行內層finally,搜索外層catch,找到合適,執行外層finally * 拋出異常在:內層D處,退出內層finally塊,搜索外層catch,找到合適,執行外層finally */ /* 使用嵌套塊的原因: * 1.修改所拋出的異常類型 * 2.在代碼的不同地方處理不同類型的異常 */ }嵌套try塊
以上代碼轉至:C#嵌套try塊工作原理
所有內層 finally 塊執行完畢後,匹配異常的那個 catch 塊中的代碼才開始執行。catch 塊中的代碼通常執行一些對異常進行處理的操作。在 catch 塊的末尾,我們有以下三個選擇。
1、重新拋出相同的異常,向調用棧高一層的代碼通知該異常的發生。
2、拋出一個不同的異常,向調用棧高一層的代碼提供更豐富的異常信息。
3、讓現成從 catch 塊的底部退出。(非終止線程,而是說執行正常地“貫穿” catch 塊的底部,並執行匹配的 finally 塊)
選擇前兩種技術將拋出異常,CLR 的行為和之前說的一樣:回溯調用棧,查找捕捉類型與拋出的異常的類型匹配的 catch 塊。
選擇後一種技術,當線程從 catch 塊的底部退出後,它將立即執行包含在 finally 塊(這個才是與 catch 關聯的 finally 塊,也就是常說的 try-catch-finally 中的finally)中的代碼。finally 塊的所有代碼執行完畢後,線程退出 finally 塊,執行緊跟 finall 塊之後的語句。如果不存在 finally 塊,線程將從最後一個 catch 塊之後的語句開始執行。
c# 允許在捕捉類型後置頂一個變量。如 ArgumentNullException e,可通過 e 查看異常的具體信息。雖然這個對象可以修改,但最好不要這麽做,而應把它當成只讀。
異常和狀態管理1