• C#中try catch中throw ex和throw方式抛出异常有何不同


    我们在C#的try catch代码块中里面经常使用throw语句抛出捕捉到的异常,但是你知道吗使用throw ex和throw抛出捕获到的异常效果是不一样的。

    异常捕捉的原理

    首先先介绍一下C#异常捕捉的原理,默认情况下在C#的一个函数中(注意这里说的是在一个函数中,不是跨多个函数),只会将最后一个异常抛出的位置记录到异常堆栈中,也就是说在一个函数中无论你用throw语句抛出了多少次异常,异常堆栈中始终记录的是函数最后一次throw异常的位置,如下面代码的函数ThrowExceptionFunction中使用throw语句抛出了4次异常,但是在46行的代码处只显示函数ThrowExceptionFunction在32行抛出了异常,之前抛出的3次异常都没有被记录到异常堆栈之中。

     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中已经修复了这个问题。

     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#只会将一个函数中最后一次抛出异常的位置记录到异常堆栈之中,那么有什么办法能将一个函数中抛出的所有异常都记录到异常堆栈中吗?答案是可以的,构造嵌套异常即可,如下代码所示:

     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()函数输出的堆栈列表之中。

    throw ex和throw

    我们知道在try catch的catch代码块捕捉到异常之后可以使用throw ex和throw将捕捉到的异常再抛出来,那么这两种写法有什么不同呢?

    throw ex

    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行三个异常。

  • 相关阅读:
    苹果将首次采用HTML5直播发布会 狼人:
    Python 3.2 alpha 2发布 狼人:
    下一代Linux文件系统Btrfs走向成熟 狼人:
    Hello! 404 狼人:
    退格回车控制台输入密码
    poj 3233 Matrix Power Series
    地址参考clang: error: linker command failed with exit code 1 (use v to see invocation)
    文本截断JQuery为textarea添加maxlength,并且兼容IE
    代码下载Html5初探视频元素video示例
    c# 限制textbox的输入范围和长度(长度不用maxlength方法)
  • 原文地址:https://www.cnblogs.com/OpenCoder/p/6208284.html
Copyright © 2020-2023  润新知