try、catch、finally詳解,你不知道的異常處理


介紹

不管是新手還是工作幾年的老油條,對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來處理異常。

性能的影響 

try{ }部分和不加try/catch語句塊的效率幾乎一樣, catch{}部分似乎需要100倍以上的時間 ,所以只要不把try{}catch{}作為你的程序的邏輯,這種設計就是合理的。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM