【轉】C# 異常處理 throw和throw ex的區別 try catch finally的執行順序(return)


  之前,在使用異常捕獲語句try...catch...throw語句時,一直沒太留意幾種用法的區別,前幾天調試程序時無意中了解到幾種使用方法是有區別的,網上一查,還真是,主要是區別在堆棧信息的起始點不同。總結如下:

一、異常處理過程

異常是發生在函數域的,函數內部發生異常:
1. 如果有try-catch處理,則跳到catch處理完畢,再繼續執行catch后面的語句塊。
2. 如果沒有異常處理,則往上拋。如果調用方也沒有異常處理,則繼續往函數棧上拋,直到最外層函數,如果應用程序級別也沒有處理,則由操作系統捕獲到程序發生錯誤,結束程序

 

二、throw幾種方式的區別
throw;可追溯到原始異常點,獲取所有異常(范圍粒度較大) (編譯器會警告,定義的ex未有使用)推薦位置2

throw ex;會將到現在為止的所有信息清空,認為你catch到的異常已經被處理了,只不過處理過程中又拋出新的異常,從而找不到真正的錯誤源。X

throw new Exception("errstr",ex);經過對異常重新包裝,會保留原始異常點信息。推薦位置1

 

三、try catch finally的執行順序(有return的情況下)

結論:
1、不管有木有出現異常,finally塊中代碼都會執行;
2、當try和catch中有return時,finally仍然會執行;
3、finally是在return后面的表達式運算后執行的(此時並沒有返回運算后的值,而是先把要返回的值保存起來,管finally中的代碼怎么樣,返回的值都不會改變,任然是之前保存的值),所以函數返回值是在finally執行前確定的;
4、finally中最好不要包含return,否則程序會提前退出,返回值不是try或catch中保存的返回值。

 

四、throw例子:

第一種(不推薦使用,可惜很多人都一直這么用的,包括俺,嘻嘻),這樣適用會吃掉原始異常點,重置堆棧中的異常起始點:

View Code
            try
            {
            }
            catch (Exception ex)
            {
                throw ex;
            }

 

第二種,可追溯到原始異常點,不過編譯器會警告,定義的ex未有使用:

View Code
            try
            {
            }
            catch (Exception ex)
            {
                throw;
            }

 

第三種,不帶異常參數的,這個同第二種其實一樣,可捕獲所有類型的異常,IDE不會告警:

View Code
            try
            {
            }
            catch
            {
                throw;
            }

 

其實第二種和第三種用法,書上也是不建議使用的,一般要從小粒度的異常捕獲開始,采用多個catch語句,大家就見仁見智吧。。。

 

第四種:經過對異常重新包裝,但是會保留原始異常點信息。推薦使用。

View Code
            try
            {
            }
            catch (Exception ex)
            {
                throw new Exception("經過進一步包裝的異常", ex);
            }

 

下面用個例子來加以說明:

View Code
  1         /// <summary>
  2         /// 入口方法
  3         /// </summary>
  4         public static void Main()
  5         {
  6             ExceptionClass ec = new ExceptionClass();
  7 
  8             try
  9             {
 10                 ec.ExceptionThrow1();
 11             }
 12             catch (Exception ex)
 13             {
 14                 Console.WriteLine(ex.ToString());
 15             }
 16 
 17             Console.WriteLine("---------------------------------------------------------------------");
 18 
 19             try
 20             {
 21                 ec.ExceptionThrow2();
 22             }
 23             catch (Exception ex)
 24             {
 25                 Console.WriteLine(ex.ToString());
 26             }
 27 
 28             Console.WriteLine("---------------------------------------------------------------------");
 29 
 30             try
 31             {
 32                 ec.ExceptionThrow3();
 33             }
 34             catch (Exception ex)
 35             {
 36                 Console.WriteLine(ex.ToString());
 37             }
 38 
 39             Console.WriteLine("---------------------------------------------------------------------");
 40 
 41             try
 42             {
 43                 ec.ExceptionThrow4();
 44             }
 45             catch (Exception ex)
 46             {
 47                 Console.WriteLine(ex.ToString());
 48             }
 49 
 50             Console.WriteLine("---------------------------------------------------------------------");
 51 
 52             Console.ReadKey();
 53         }
 54     }
 55 
 56     /// <summary>
 57     /// 該Class用來測試異常拋出時相關上下文棧的調用情況
 58     /// </summary>
 59     public class ExceptionClass
 60     {
 61         /// <summary>
 62         /// 拋出異常方法
 63         /// </summary>
 64         public void ExceptionThrow1()
 65         {
 66             try
 67             {
 68                 // 調用原始異常拋出方法來拋出異常
 69                 this.ExceptionMethod();
 70             }
 71             catch (Exception ex)
 72             {
 73                 throw ex;
 74             }
 75         }
 76 
 77         /// <summary>
 78         /// 拋出異常方法1
 79         /// </summary>
 80         public void ExceptionThrow2()
 81         {
 82             try
 83             {
 84                 this.ExceptionMethod();
 85             }
 86             catch (Exception ex)
 87             {
 88                 throw;
 89             }
 90         }
 91 
 92         /// <summary>
 93         /// 拋出異常方法2
 94         /// </summary>
 95         public void ExceptionThrow3()
 96         {
 97             try
 98             {
 99                 this.ExceptionMethod();
100             }
101             catch
102             {
103                 throw;
104             }
105         }
106 
107         /// <summary>
108         /// 拋出異常方法3
109         /// </summary>
110         public void ExceptionThrow4()
111         {
112             try
113             {
114                 this.ExceptionMethod();
115             }
116             catch (Exception ex)
117             {
118                 throw new Exception("經過進一步包裝的異常", ex);
119             }
120         }
121 
122         /// <summary>
123         /// 原始異常拋出方法
124         /// </summary>
125         private void ExceptionMethod()
126         {
127             throw new DivideByZeroException();
128         }
129    }

 

運行結果如下:

 

從運行的結果可以看到,第一種用法已經吃掉了原始異常信息。而其它3種用法都可以追溯到原始異常,推薦使用第四種用法,希望大家能了解這些細微的差別,享受Coding的樂趣吧...


五、try catch finally 舉例:
情況1:try{} catch(){}finally{} return;
            顯然程序按順序執行。
情況2:try{ return; }catch(){} finally{} return;
          程序執行try塊中return之前(包括return語句中的表達式運算)代碼;
         再執行finally塊,最后執行try中return;
         finally塊之后的語句return,因為程序在try中已經return所以不再執行。
情況3:try{ } catch(){return;} finally{} return;
         程序先執行try,如果遇到異常執行catch塊,
         有異常:則執行catch中return之前(包括return語句中的表達式運算)代碼,再執行finally語句中全部代碼,
                     最后執行catch塊中return. finally之后也就是4處的代碼不再執行。
         無異常:執行完try再finally再return.
情況4:try{ return; }catch(){} finally{return;}
          程序執行try塊中return之前(包括return語句中的表達式運算)代碼;
          再執行finally塊,因為finally塊中有return所以提前退出。
情況5:try{} catch(){return;}finally{return;}
          程序執行catch塊中return之前(包括return語句中的表達式運算)代碼;
          再執行finally塊,因為finally塊中有return所以提前退出。
情況6:try{ return;}catch(){return;} finally{return;}
          程序執行try塊中return之前(包括return語句中的表達式運算)代碼;
          有異常:執行catch塊中return之前(包括return語句中的表達式運算)代碼;
                       則再執行finally塊,因為finally塊中有return所以提前退出。
          無異常:則再執行finally塊,因為finally塊中有return所以提前退出。

最終結論:任何執行try 或者catch中的return語句之前,都會先執行finally語句,如果finally存在的話。
                  如果finally中有return語句,那么程序就return了,所以finally中的return是一定會被return的,
                  編譯器把finally中的return實現為一個warning。

 

 

下面是個測試程序
public class FinallyTest  
{
	public static void main(String[] args) {
		 
		System.out.println(new FinallyTest().test());;
	}

	static int test()
	{
		int x = 1;
		try
		{
			x++;
			return x;
		}
		finally
		{
			++x;
		}
	}
}
結果是2。
分析:
 在try語句中,在執行return語句時,要返回的結果已經准備好了,就在此時,程序轉到finally執行了。 在轉去之前,try中先把要返回的結果存放到不同於x的局部變量中去,執行完finally之后,在從中取出返回結果, 因此,即使finally中對變量x進行了改變,但是不會影響返回結果。 它應該使用棧保存返回值。
 


免責聲明!

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



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