介紹 不管是新手還是工作幾年的老油條,對try{}catch{}來說是不陌生的。他可以來幫助我們獲取異常信息,在try中的代碼出現錯誤,火災catch代碼塊中被捕獲到。官方也給了詳細的解釋:。 拋出異常時,公共語言運行庫(CLR)會查找catch處理此異常的語句。如果當前正在執行的方法不包含這樣的c ...
介紹
不管是新手還是工作幾年的老油條,對try{}catch{}來說是不陌生的。他可以來幫助我們獲取異常信息,在try中的代碼出現錯誤,火災catch代碼塊中被捕獲到。官方也給了詳細的解釋:。
拋出異常時,公共語言運行庫(CLR)會查找catch
處理此異常的語句。如果當前正在執行的方法不包含這樣的catch
塊,則CLR會查看調用當前方法的方法,依此類推調用堆棧。如果未catch
找到任何塊,則CLR向用戶顯示未處理的異常消息並停止執行該程式。
以上的這些基礎我們可能都瞭解。但是你真的瞭解他的運行步驟嗎?我就是帶著這個疑問進行了一下的幾個測試。
簡單示例:
既然有了疑問就帶著疑問想辦法驗證吧,下麵我們通過多個例子來一步一步的分析得到我們想要的結果。
簡單的try catch
首先是一個try中沒有異常的示例:
static void Main(string[] args) { string result =GetStr(); Console.WriteLine(result); Console.ReadLine(); } public static string GetStr() { try { Console.WriteLine("走到:try"); return "這裡是try返回值"; } catch (Exception e) { Console.WriteLine("走到:catch"); return "這裡是catch返回值"; } finally { Console.WriteLine("走到:finally"); } return "這裡是方法底部返回值"; }
運行結果:
執行分析:
這是最簡單最常見的示例,沒有發生異常,然後沒有走catch,執行順序是try=>finally=>return;
所有我們得到一個還不確定的結果在GetStr方法中不會執行方法自己的return;
但是finally方法塊都會執行;
來個異常的:
下麵我們讓try方法塊出錯就好了,然後我們修改一下代碼如下:
public static string GetStr() { try { int value = 0; Console.WriteLine("走到:try"); var i = 1 / value;//這裡會出錯 0不能被整除 return "這裡是try返回值"; } catch (Exception e) { Console.WriteLine("走到:catch"); return "這裡是catch返回值"; } finally { Console.WriteLine("走到:finally"); } return "這裡是方法底部返回值"; }
運行結果:
執行分析:
這裡在try發生了異常,然後沒有正常返回,進入到了catch方法塊:try=>catch=>finally=>return;
這裡我們可以確定:
- 不管try有沒有出錯finally方法塊都會被執行。【快記筆記,知識點。】
- 就算try和catch方法都有return,finally都會執行;
- 只要try或者catch return返回,try catch 之外的return都無效;
說到這裡有些不懂得人可能會有疑問?那在finally寫個return是什麼結果哪?很不幸的告訴你,不能這麼寫,寫了會怎麼樣,哼會提示:控制不能離開finally子句主體;
驗證return的值
上面我們知道了怎樣都會執行finally,但是執行了finally對我們的正返回值有沒有印象哪,例如我在try裡面對一個變數賦值為a字元串,進行了返回,但是在finally裡面修改成了b字元串。會不會被修改哪?
我們還是老代碼,然後修改成我們想的樣子:
public static string GetStr() { string str = ""; try { str = "修改成了a"; Console.WriteLine("走到:try"); // return "這裡是try返回值"; return str; } catch (Exception e) { Console.WriteLine("走到:catch"); return "這裡是catch返回值"; } finally { str = "修改成了b"; Console.WriteLine("走到:finally"); } return "這裡是方法底部返回值"; }
運行結果:
執行分析:
沒有異常還是老樣子:執行順序是try=>finally=>return;
但是我們在finally修改了str字元串,但是通過輸出結果我們得到的還是a字元串,
所有我們得到結論:雖然finally方法會被執行但是,返回結果不會被改變,也就是如果finally是在return之後執行的那麼他會把返回結果先保存起來,然後不管finally代碼執行了什麼,都不會影響到返回結果,等finally執行完成在返回結果。
多個重覆try
那麼我們可以寫多個try{}try{}這樣的語句嗎?不行,會直接報錯,其實這樣寫沒有任何意義。
多個重覆catch
那麼重覆多個catch哪?這個是可以的例如下麵我這樣:
try { str = "修改成了a"; Console.WriteLine("走到:try"); // return "這裡是try返回值"; return str; } catch(InvalidCastException e) { } catch (Exception e) { Console.WriteLine("走到:catch"); return "這裡是catch返回值"; }
這個是被允許的,因為這是有意義的寫法。
開始升級
為什麼要一定寫try-catch-finally 我只寫其中一部分不可以嗎?
try-catch
那麼我們這次不寫finally試一試吧。try方法塊沒有異常已經不用測了,因為上面我們已經確認過了。會返回try的內容。那麼就try異常吧。
public static string GetStr() { try { Console.WriteLine("走到:try"); int value = 0; int s = 1 / value; return "這裡是try返回值"; } catch (Exception e) { Console.WriteLine("走到:catch"); return "這裡是catch返回值"; } return "這裡是方法底部返回值"; }
運行結果:
執行分析:
通過可以正常運行我們知道這樣寫一點問題都沒有,所以結果就是
- finally也不是必須的。
- 如果catch沒有return 就會返回底部return方法。這是我們的常識。
這樣做有什麼作用或者意義哪,通常我們可以上面說的定義多個catch來檢測異常,還有一個用途就是忽略異常,就是這種異常你系統可以被運行,就可以catch內不寫return正常跳過異常執行下麵的方法體。但是不是很被建議,
try-finally
那麼try-finally哪,這樣寫也是被允許的。
這樣單獨寫第一就是在finally語句塊內做try的資源釋放。正常情況下try沒有異常,在finally中處理try語句塊的資源釋放。
第二就是try發生了異常,其實finally起到的作用還是一樣的。但是這裡區別在於如果異常未經處理,可能就導致程式退出了。所有執不執行已經無所謂了。我們來個異常示例:
static void Main(string[] args) { string result = ""; try { result = GetStr(); } catch (Exception e) { Console.WriteLine("主方法catch:"); } Console.WriteLine(result); Console.ReadLine(); } public static string GetStr() { try { Console.WriteLine("走到:try"); int value = 0; int s = 1 / value; return "這裡是try返回值"; } finally { Console.WriteLine("走到:finally"); } return "這裡是方法底部返回值"; }View Code
運行結果:
執行分析:
try發生了異常,但是因為finally始終都會執行所有也會執行,然後異常被調用方法內的catch捕獲執行順序:try=>finally=>catch(主方法)
所有我們得到結果:
- try-finally可以運行
- try如果沒有catch但是發生異常會向上找catch方法塊來捕獲。知道沒有系統崩潰。
以上的例子都是非控制(系統出現異常就自動拋出了)的拋出異常,那麼我們可以控制異常的拋出點嗎?當然可以。
throw
還是老習慣先上官方解釋,發出程式執行期間出現異常的信號。
到底什麼意思哪,我個人理解就是一個告訴你是不是出現異常的標誌,就像信號燈一樣,亮了什麼顏色就代表著什麼意思 ,當然就是打個比方。信號燈一定是對的,但是這個可不是啊。
簡單來總結他就兩個功能:第一是告訴別人有異常,第二就是重新發出異常。
告訴別人有異常
簡單來說就是自己可以定義一個異常,然後給上層代碼處理。(我就是在這想告訴你有異常)
static void Main(string[] args) { string result = ""; try { Console.WriteLine("主方法try:"); result = GetStr(6); } catch (IndexOutOfRangeException e) { Console.WriteLine($"主方法catch拋出異常:{e.GetType().Name}"); } Console.WriteLine("主方法結束"); Console.ReadLine(); } public static string GetStr(int index) { if (index < 0 || index > 5) { Console.WriteLine("進入異常:"); throw new IndexOutOfRangeException(); } return "正確返回"; }View Code
運行結果:
執行分析:
在主方法里調用GetStr方法,然後傳入了6判斷進入if然後給自己給出了異常,退出當前程式進入主方法捕獲異常catch中,捕獲到異常列印。這裡就展示了自己在某種情況下定義一個異常然後給上層拋出。
重新引發異常
這個與上面有什麼不同哪,功能都是一樣的,但是效果卻不一樣,這個是我發生了異常但是我不處理,我在繼續告訴別人讓別人處理。下麵我們只需要把上面的GetStr方法修改成這樣:
public static string GetStr(int index) { try { if (index < 0 || index > 5) { Console.WriteLine("進入異常:"); throw new IndexOutOfRangeException(); } } catch (Exception e) { Console.WriteLine($"進入異常catch重新拋出異常:{e.GetType().Name}"); throw; } return "正確返回"; }View Code
運行結果:
執行分析:
在主方法里調用GetStr方法,然後傳入了6判斷進入if然後給自己給出了異常,在GetStr方法內的catch捕獲到異常,但是他沒有處理,有重新使用Throw來引發異常,把異常傳到了上層(主方法),最後還是主方法的catch來處理異常。