咱们在C#的try catch代码块中里面常常使用throw语句抛出捕捉到的异常,可是你知道吗使用throw ex和throw抛出捕获到的异常效果是不同的。函数
异常捕捉的原理spa
首先先介绍一下C#异常捕捉的原理,默认状况下在C#的一个函数中(注意这里说的是在一个函数中,不是跨多个函数),只会将最后一个异常抛出的位置记录到异常堆栈中,也就是说在一个函数中不管你用throw语句抛出了多少次异常,异常堆栈中始终记录的是函数最后一次throw异常的位置,以下面代码的函数ThrowExceptionFunction中使用throw语句抛出了4次异常,可是在46行的代码处只显示函数ThrowExceptionFunction在32行抛出了异常,以前抛出的3次异常都没有被记录到异常堆栈之中。.net
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace ExceptionTest2 7 { 8 class Program 9 { 10 static void ThrowExceptionFunction() 11 { 12 try 13 { 14 try 15 { 16 try 17 { 18 throw new Exception("模拟异常"); 19 } 20 catch (Exception ex1) 21 { 22 throw; 23 } 24 } 25 catch (Exception ex2) 26 { 27 throw; 28 } 29 } 30 catch (Exception ex3) 31 { 32 throw; 33 } 34 35 } 36 37 38 static void Main(string[] args) 39 { 40 try 41 { 42 ThrowExceptionFunction(); 43 } 44 catch (Exception ex) 45 { 46 Console.WriteLine(ex.StackTrace);//由于C#会为每一个函数的异常记录一次堆栈信息,而本例中有两个函数分别为ThrowExceptionFunction和Main,因此这里堆栈捕捉到了两个异常一个是在函数ThrowExceptionFunction中32行,另外一个是Main函数中42行, 47 } 48 49 Console.ReadLine(); 50 } 51 } 52 }
在.net framework3.5及以前,函数中catch代码块抛出的异常没法准确捕捉到位置,以下面代码中Main函数最后一次抛出异常是在代码20行,可是在25行输出的信息中却显示异常是在代码29行抛出的,这应该是.net framework3.5及以前的一个BUG,在.net framework4.0中已经修复了这个问题。code
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace ExceptionTest3 7 { 8 class Program 9 { 10 static void Main(string[] args) 11 { 12 try 13 { 14 try 15 { 16 throw new Exception("异常模拟"); 17 } 18 catch (Exception ex1) 19 { 20 throw; 21 } 22 } 23 catch (Exception ex2) 24 { 25 Console.WriteLine(ex2.StackTrace); 26 } 27 28 Console.ReadLine(); 29 } 30 } 31 }
上面咱们说了C#只会将一个函数中最后一次抛出异常的位置记录到异常堆栈之中,那么有什么办法能将一个函数中抛出的全部异常都记录到异常堆栈中吗?答案是能够的,构造嵌套异常便可,以下代码所示:blog
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace ExceptionTest4 7 { 8 class Program 9 { 10 static void ThrowExceptionFunction() 11 { 12 try 13 { 14 try 15 { 16 try 17 { 18 throw new Exception("模拟异常1"); 19 } 20 catch (Exception ex1) 21 { 22 throw new Exception("模拟异常2", ex1); 23 } 24 } 25 catch (Exception ex2) 26 { 27 throw new Exception("模拟异常3", ex2); 28 } 29 } 30 catch (Exception ex3) 31 { 32 throw new Exception("模拟异常4", ex3); 33 } 34 35 } 36 37 38 static void Main(string[] args) 39 { 40 try 41 { 42 ThrowExceptionFunction(); 43 } 44 catch (Exception ex) 45 { 46 Console.WriteLine(ex.ToString());//要想输出函数ThrowExceptionFunction内抛出的全部异常,将ThrowExceptionFunction内部的异常都嵌套封装便可,而后在输出异常的时候使用ex.ToString()函数,就能够输出全部嵌套异常的堆栈信息 47 } 48 49 Console.ReadLine(); 50 } 51 } 52 }
上面代码中咱们在函数ThrowExceptionFunction中将四个throw出来的异常都嵌套封装了,最后在Main函数中使用ex.ToString()函数便可输出完整的异常堆栈,在ThrowExceptionFunction函数中抛出的全部异常都显示在了ex.ToString()函数输出的堆栈列表之中。string
throw ex和throwit
咱们知道在try catch的catch代码块捕捉到异常以后可使用throw ex和throw将捕捉到的异常再抛出来,那么这两种写法有什么不一样呢?io
throw exclass
throw ex这种写法会让C#重置异常的抛出点,咱们来看这段代码:原理
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace ExceptionTesting 8 { 9 class Program 10 { 11 static void InnerException() 12 { 13 throw new Exception("模拟异常"); 14 } 15 16 static void OuterException() 17 { 18 try 19 { 20 InnerException(); 21 } 22 catch (Exception ex) 23 { 24 throw ex; 25 } 26 } 27 28 static void Main(string[] args) 29 { 30 try 31 { 32 OuterException(); 33 } 34 catch (Exception ex) 35 { 36 Console.WriteLine(ex.StackTrace);//因为代码24行使用throw ex重置了异常抛出点,因此这里异常堆栈只能捕捉到代码32行和24行抛出的异常,可是13行的异常在堆栈中没法捕捉到 37 } 38 39 Console.ReadLine(); 40 } 41 } 42 }
能够看到使用throw ex会使得C#重置代码中异常的抛出点,从而让C#认为异常的原始抛出点应该是在代码24行,在异常堆栈中没法捕捉到代码13行所抛出的异常。
throw
使用throw和throw ex惟一的不一样就是throw并不会让C#重置异常的抛出点,咱们将上面代码中24行的throw ex改成throw以下:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace ExceptionTesting 8 { 9 class Program 10 { 11 static void InnerException() 12 { 13 throw new Exception("模拟异常"); 14 } 15 16 static void OuterException() 17 { 18 try 19 { 20 InnerException(); 21 } 22 catch(Exception ex) 23 { 24 throw; 25 } 26 } 27 28 static void Main(string[] args) 29 { 30 try 31 { 32 OuterException(); 33 } 34 catch(Exception ex) 35 { 36 Console.WriteLine(ex.StackTrace);//因为如今代码24行使用了throw抛出捕获到的异常,并无重置原始异常的抛出点,因此这里异常堆栈不但能捕捉到代码32行和24行抛出的异常,还能捕捉到代码13行抛出的异常。 37 } 38 39 Console.ReadLine(); 40 } 41 } 42 }
因为这一次咱们使用了throw来抛出代码24行中catch代码块中捕获到的异常,并无重置异常的抛出点,所以在上面代码36行这一次异常堆栈输出了13行、24行、32行三个异常。