[C#] C# 知識回憶 - 你真的懂異常(Exception)嗎?。本站提示廣大學習愛好者:([C#] C# 知識回憶 - 你真的懂異常(Exception)嗎?)文章只能為提供參考,不一定能成為您想要的結果。以下是[C#] C# 知識回憶 - 你真的懂異常(Exception)嗎?正文
我們平常在寫順序時,有意中(或技術不夠),而招致順序運轉時呈現不測(或異常),關於這個問題, C# 有專門的異常處置順序。 異常處置所觸及到的關鍵字有 try
、catch
和 finally
等,用來處置失敗的狀況。 CLR、.NET 本身的類庫、其它第三方庫或許你寫的順序代碼都有能夠會呈現異常。當然,你也可以直接運用 throw ,經過顯式的方式來停止創立異常。
在你的代碼中呈現異常的時分,順序會找到並執行最先婚配的 catch
塊。 假如在調用堆棧中的恣意地位中,異常處置順序都沒有找到適宜(你寫的)的 catch
塊,就會自動終止該進程,並向用戶顯示(拋出)一條錯誤的信息。
在這裡我寫了個被 0 除會呈現的異常(一個顯式引發 DivideByZeroException 異常)並捕捉該異常的示例:
1 /// <summary> 2 /// 除法 3 /// </summary> 4 /// <param name="x"></param> 5 /// <param name="y"></param> 6 /// <returns></returns> 7 static double Division(double x, double y) 8 { 9 if (y == 0) 10 { 11 throw new DivideByZeroException(); 12 } 13 14 return x / y; 15 } 16 17 static void Main(string[] args) 18 { 19 //定義兩個變量 x, y 20 double x = 250, y = 0; 21 22 try 23 { 24 var result = Division(x, y); 25 Console.WriteLine($"result: {result}"); 26 } 27 catch (DivideByZeroException e) 28 { 29 30 Console.WriteLine(e); 31 } 32 33 Console.Read(); 34 }
一切異常類型(包括自定義的異常)都是由基類 Exception
派生的。
運用 try
塊包圍你以為能夠會呈現異常的代碼。
一旦 try
塊中發作異常,控制流將按順序找到與之關聯的 catch 塊,假如沒有找到適宜的,就會引發最終的異常基類 Exception 內的處置順序(前提你曾經 catch)。
假如呈現異常卻沒有對應的異常處置順序,則該順序將會中止執行,並拋出對應錯誤的信息。
在 catch
定義了的異常變量,可以獲取對應異常類型的信息。比方調用堆棧的形態和錯誤的闡明,詳細看 Excetion 的屬性。
throw
關鍵字可以顯式引發異常。
即便呈現異常也會執行 finally
塊中的代碼。普通來說,我們會運用 finally
塊釋放資源,例如,封閉xx流。
順序在運轉時呈現的錯誤,會不時在順序中停止傳達,這種機制稱為“異常”。 異常通常由錯誤的代碼引發,並由可以更正錯誤的代碼停止 catch。 異常也可以由 .NET 的 CLR 或由順序中的代碼引發, 一旦引發了異常,這個異常將會在調用堆棧中不斷向上停止傳達,直到尋覓到跟它婚配的 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 }
在引發異常之後,運轉時順序會反省以後語句確定它能否包括在 try
塊中。 假如是的話,就會反省與該 try
塊相關聯的一切 catch
塊,來確定它們能否可以 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 }
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
塊之前,CLR 會反省 finally
塊。 finally
塊使順序員可以肅清中止的 try
塊能夠遺留下的任何模糊形態,或許釋聽任何內部資源(例如圖形句柄、db 銜接或 IO 流),而無需等候 CLR 中的渣滓回收器終結這些對象。 例如:
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 流的語句,你可以嘗試下去掉正文再看看後果,記住哦,IO 操作都應該在完畢時釋放資源。
假如 WriteByte(0)(第9行)
引發了異常,那麼在沒有調用 fs.Close()
的狀況下,你在第二個 try
塊中嘗試重新 OpenWrit() 的代碼就會失敗,由於此時文件會堅持鎖定形態。 假設你取消正文,由於會執行 finally
塊(即便已引發異常),使得可以正確地封閉文件,從而防止再次引發異常。
假如在引發異常之後沒有在調用堆棧上找到相婚配的 catch
塊,則會能夠會呈現上面的狀況:
假如異常呈現在析構函數中,則中止該析構函數並調用基類的析構函數(假如有)。
假如調用堆棧包括靜態結構函數或靜態字段初始值設定項,則會引發 TypeInitializationException,並將原始異常分配給新異常的 InnerException 屬性。
假如抵達線程的掃尾,將會終止線程。
你可以運用 try 塊來對你覺得能夠會呈現異常的代碼停止分區。 其中,與之關聯的 catch 塊可用於處置任何異常狀況。 一個包括代碼的 finally 塊,無論 try
塊中能否在運轉時引發異常(例如,釋放在 try
塊中分配的資源),這些 finally 塊的代碼都會運轉。 這些“異常局部”:可以由一個 try
塊、一個或多個關聯的 catch
塊、一個 finally
塊辨別組合。
這裡我羅列了 3 種狀況:一個 try-catch
語句,一個 try-finally
語句,和一個 try-catch-finally
語句。
(1)try-catch:
1 static void Main(string[] args) 2 { 3 try 4 { 5 //需求執行的代碼 6 } 7 catch (Exception e) 8 { 9 //這裡可以獲取到被捕捉的異常 10 //你需求知道自己應該如何處置該異常 11 } 12 }
(2)try-finally:
1 try 2 { 3 //需求執行的代碼 4 } 5 finally 6 { 7 //在 try 塊後執行的代碼 8 }
(3)try-catch-finally:
1 try 2 { 3 //需求執行的代碼 4 } 5 catch (Exception e) 6 { 7 //這裡處置異常 8 } 9 finally 10 { 11 //在 try 塊(也能夠是 catch 塊)後執行的代碼 12 }
【備注】不帶有 catch
或 finally
塊的 try
塊將招致編譯器錯誤。
catch
塊可以指定要捕獲的異常類型,又可以稱為“異常挑選器”。 異常類型都是從 Exception 派生出來。 普通而言,不會將一切異常的基類 System.Exception 指定為要 catch 的“異常挑選器”,除非你十分理解如何處置由 try
塊引發的一切異常,或許在 catch
塊中包括了 throw 語句。
多個 catch
塊可以串聯在一同(要求異常挑選器不同)。 多個 catch
塊的執行順序是:在代碼中,從頂部究竟部,但是,關於在運轉時所引發的每一個異常,順序都只會執行一個 catch
數據塊。 與指定的異常類型或它的基類相婚配的第一個 catch
塊,才會被執行。 通常,我們需求將最特殊(最詳細或許說派生水平最最最高)的異常類,這段 catch
塊放在一切 catch 塊的最後面,而他們的基類 Excetion 的 catch 塊就放在最後(當然,也可以不寫)。
在以下條件為真時,你應該選擇 catch 異常:
理解引發異常的緣由,並可完成有選擇性的恢復。例如,在捕捉 FileNotFoundException 時你可以提示用戶“文件找不到”和“請輸出新的文件名”等。
你也可以新建一個更詳細或許說更具有代表性的異常,並選擇引發該異常。
1 double GetNum(double[] nums,int index) 2 { 3 try 4 { 5 return nums[index]; 6 } 7 catch (IndexOutOfRangeException e) 8 { 9 throw new ArgumentOutOfRangeException("Sorry, 你想要的索引曾經超出界線!"); 10 } 11 }
希望在將異常拋出去時,我們通常會選擇處置局部異常。 在上面這個示例中,catch
塊在再次 throw 異常之前,添加錯誤日志。
1 try 2 { 3 //嘗試訪問零碎資源 4 } 5 catch (Exception e) 6 { 7 //偽代碼:記載錯誤日志 8 log.Error(e); 9 10 //再重新拋出錯誤 11 throw; 12 }
可以運用 finally
塊釋放(清算)在 try
塊中需求執行釋放(清算)資源的操作。 假如存在finally
塊,它將在最後執行,也就是在 try
塊和任何婚配 catch
塊之後執行。 不論能否引發異常或許說能否找到與異常類型相婚配的 catch
塊,finally
塊它一直都會運轉。
可以運用 finally
塊釋放資源(如 IO 流、DB 銜接和圖形句柄),而不要等候運轉時中的渣滓回收器來完成對象資源的回收。 其實,我們更建議運用 using 語句。
在上面的示例中,我運用 finally
塊封閉在 try
塊中翻開的文件。留意,在封閉文件之前你應該要反省該文件句柄的形態。 假如 try
塊無法翻開文件,則文件句柄的值仍然為 null
,這時, finally
塊就不會嘗試封閉它。 或許說,假如在 try
塊中成功翻開該文件,則 finally
塊才會成功地封閉正在翻開的文件。
1 static void Main(string[] args) 2 { 3 FileStream fs = null; 4 FileInfo fi = new System.IO.FileInfo("C:\\小二和小三的故事.txt"); 5 6 try 7 { 8 fs = fi.OpenWrite(); 9 fs.WriteByte(0); 10 } 11 finally 12 { 13 // 記得判別 null 哦,不然能夠觸發其它異常 14 if (fs != null) 15 { 16 fs.Close(); 17 } 18 } 19 20 }
《C# 知識回憶 - 序列化》
《C# 知識回憶 - 表達式樹 Expression Trees》
《C# 知識回憶 - 特性 Attribute》、《分析 AssemblyInfo.cs - 理解常用的特性 Attribute》《C# 知識回憶 - 委托 delegate》、《C# 知識回憶 - 委托 delegate (續)》
《C# 知識回憶 - 事情入門》、《C# 知識回憶 - Event 事情》
《string 與 String,大 S 與小 S 之間沒有什麼不可言說的機密》
【博主】反骨仔
【出處】http://www.cnblogs.com/liqingwen/p/6206251.html
【參考】微軟官方文檔