學會使用異常
異常:指的是我們寫的程式在運作時出現了錯誤,并且它會不斷的蔓延、傳播和擴散,有點像病毒一樣。
異常通常由錯誤的代碼引發,可能是使用者的錯誤輸入,可能是一方沒有按照約定來傳輸格式,也可能是資料傳輸的過程中被篡改。我們會對自己認為有可能報錯的代碼進行 catch ,這稱為捕獲異常。
一旦引發了異常,這個異常将會在調用堆棧中一直向上進行傳播,直到尋找到跟它比對的
catch
語句。沒有 catch 的異常會由系統提供的預設的異常處理程式進行處理,也就是你經常看到的一個突然造成調試中斷并顯示異常資訊的對話框。
所有的異常,它都是從 Exception 派生出來的,他們都包含了詳細的異常描述屬性。在這裡我将自定義了一個新的異常類,然後使用
throw
關鍵字顯式引發該對象(即異常)。
1 /// <summary>
2 /// 定義新異常
3 /// </summary>
4 class MyException : Exception
5 {
6 public MyException(string msg) { }
7 }
8
9 /// <summary>
10 /// 抛出新定義的異常
11 /// </summary>
12 static void ThrowMyExcetion()
13 {
14 throw new MyException("Sorry, this is test!");
15 }
在引發異常之後,CLR 運作時程式會檢查目前語句确定它是否包含在
try
塊中。 如果是的話,就會檢查與該
try
塊相關聯的所有
catch
塊,來确定它們是否能夠 catch 該異常。如果該
catch
塊的類型與異常或它的基類的相同(或比對),則該
catch
塊就能夠捕獲并處理。
1 static void Main(string[] args)
2 {
3 try
4 {
5 ThrowMyExcetion(); //直接調用抛出異常的方法
6 }
7 catch (MyException e)
8 {
9 Console.WriteLine(e);
10 }
11
12 Console.Read();
13 }
如果引發異常的語句不在
try
塊中,或者包含該語句的
try
塊沒有比對的
catch
塊,CLR 運作時将檢查調用方法中是否有合适
try
語句和
catch
塊。 運作時将在調用堆棧中繼續往上搜尋相容(或比對)的
catch
塊。在找到并執行
catch
塊之後,控制權将傳遞給
catch
塊之後的下一個語句。
一個
try
語句可能包含多個
catch
塊。 将執行第一個能夠處理該異常的
catch
語句;任何後續的
catch
語句都将被忽略。 是以,在任何情況下都應該按照從最具體(或者派生程度最高)到最不具體這一順序排列 catch 塊。 例如:
1 static void Main(string[] args)
2 {
3 StreamWriter sw = null;
4
5 try
6 {
7 sw = new StreamWriter(@"C:\book\小二和小三的故事.txt");
8 sw.Write("You are 250.");
9 }
10 catch (FileNotFoundException e)
11 {
12 //将具體的異常放在第一位
13 Console.WriteLine(e);
14 }
15 catch (IOException e)
16 {
17 //将并不具體的放在相對後面的位置
18 Console.WriteLine(e);
19 }
20 catch (Exception e)
21 {
22 Console.WriteLine(e);
23 }
24 finally
25 {
26 if (sw != null)
27 {
28 sw.Close();
29 }
30 }
31
32 Console.Read();
33 }
執行
catch
塊之前,運作時會檢查
finally
塊。
Finally
塊使程式員能夠清除中止的
try
塊可能遺留下的任何模糊狀态,或者釋放任何外部資源(例如圖形句柄、資料庫連接配接或檔案流),而無需等待運作時中的垃圾回收器終結這些對象。 例如:
1 static void Main(string[] args)
2 {
3 FileStream fs = null;
4 FileInfo fi = new FileInfo(@"小二和小三的故事.txt");
5
6 try
7 {
8 fs = fi.OpenWrite();
9 fs.WriteByte(0);
10 }
11 finally
12 {
13 //記住哦,如果你忘記 close,将會引發 IO 異常!
14 //if (fs != null)
15 //{
16 // fs.Close();
17 //}
18 }
19
20 try
21 {
22 fs = fi.OpenWrite();
23 fs.WriteByte(1);
24 Console.WriteLine("OK!");
25 }
26 catch (IOException e)
27 {
28 Console.WriteLine("Fail!");
29 }
30
31 Console.Read();
32 }
“Fail!”,這是因為上面注釋了需要關閉檔案流的語句,你可以嘗試下去掉注釋看看結果,記住哦,IO 操作都應該在結束時釋放資源。
如果
WriteByte(0)(第9行)
引發了異常,那麼在沒有調用 fs
.Close()
的情況下,你在第二個
try
塊中嘗試重新 OpenWrit() 的代碼就會失敗,因為此時檔案會保持鎖定狀态。 假如你取消注釋,由于會執行
finally
塊(即使已引發異常),使得可以正确地關閉檔案,進而避免再次引發異常。
如果在引發異常之後沒有在調用堆棧上找到相比對的
catch
塊,則:
- 如果異常出現在析構函數中,則中止該析構函數并調用基類的析構函數(如果有)。
- 如果調用堆棧包含靜态構造函數或靜态字段初始值設定項,則會引發 TypeInitializationException,并将原始異常配置設定給新異常的 InnerException 屬性。
- 如果到達線程的開頭,将會終止線程。
C# 基礎回顧系列
《C# 知識回顧 - 序列化》
《C# 知識回顧 - 表達式樹 Expression Trees》
《C# 知識回顧 - 特性 Attribute》、《剖析 AssemblyInfo.cs - 了解常用的特性 Attribute》
《C# 知識回顧 - 委托 delegate》、《C# 知識回顧 - 委托 delegate (續)》
《C# 知識回顧 - 事件入門》、《C# 知識回顧 - Event 事件》
《string 與 String,大 S 與小 S 之間沒有什麼不可言說的秘密》
《C# 知識回顧 - 異常介紹》
【部落客】反骨仔
【出處】http://www.cnblogs.com/liqingwen/p/6193534.html
【參考】https://docs.microsoft.com/zh-cn/dotnet/articles/csharp/programming-guide/exceptions/using-exceptions
【參考】微軟官方文檔