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

异常捕捉的原理

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

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace ExceptionTest2
{
class Program
{
static void ThrowExceptionFunction()
{
try
{
try
{
try
{
throw new Exception("模拟异常");
}
catch (Exception ex1)
{
throw;
}
}
catch (Exception ex2)
{
throw;
}
}
catch (Exception ex3)
{
throw;
} } static void Main(string[] args)
{
try
{
ThrowExceptionFunction();
}
catch (Exception ex)
{
Console.WriteLine(ex.StackTrace);//因为C#会为每个函数的异常记录一次堆栈信息,而本例中有两个函数分别为ThrowExceptionFunction和Main,所以这里堆栈捕捉到了两个异常一个是在函数ThrowExceptionFunction中32行,另一个是Main函数中42行,
} Console.ReadLine();
}
}
}

在.net framework3.5及之前,函数中catch代码块抛出的异常无法准确捕捉到位置,如下面代码中Main函数最后一次抛出异常是在代码20行,但是在25行输出的信息中却显示异常是在代码29行抛出的,这应该是.net framework3.5及之前的一个BUG,在.net framework4.0中已经修复了这个问题。

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace ExceptionTest3
{
class Program
{
static void Main(string[] args)
{
try
{
try
{
throw new Exception("异常模拟");
}
catch (Exception ex1)
{
throw;
}
}
catch (Exception ex2)
{
Console.WriteLine(ex2.StackTrace);
} Console.ReadLine();
}
}
}

上面我们说了C#只会将一个函数中最后一次抛出异常的位置记录到异常堆栈之中,那么有什么办法能将一个函数中抛出的所有异常都记录到异常堆栈中吗?答案是可以的,构造嵌套异常即可,如下代码所示:

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace ExceptionTest4
{
class Program
{
static void ThrowExceptionFunction()
{
try
{
try
{
try
{
throw new Exception("模拟异常1");
}
catch (Exception ex1)
{
throw new Exception("模拟异常2", ex1);
}
}
catch (Exception ex2)
{
throw new Exception("模拟异常3", ex2);
}
}
catch (Exception ex3)
{
throw new Exception("模拟异常4", ex3);
} } static void Main(string[] args)
{
try
{
ThrowExceptionFunction();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());//要想输出函数ThrowExceptionFunction内抛出的所有异常,将ThrowExceptionFunction内部的异常都嵌套封装即可,然后在输出异常的时候使用ex.ToString()函数,就可以输出所有嵌套异常的堆栈信息
} Console.ReadLine();
}
}
}

上面代码中我们在函数ThrowExceptionFunction中将四个throw出来的异常都嵌套封装了,最后在Main函数中使用ex.ToString()函数即可输出完整的异常堆栈,在ThrowExceptionFunction函数中抛出的所有异常都显示在了ex.ToString()函数输出的堆栈列表之中。

throw ex和throw

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

throw ex

throw ex这种写法会让C#重置异常的抛出点,我们来看这段代码:

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace ExceptionTesting
{
class Program
{
static void InnerException()
{
throw new Exception("模拟异常");
} static void OuterException()
{
try
{
InnerException();
}
catch (Exception ex)
{
throw ex;
}
} static void Main(string[] args)
{
try
{
OuterException();
}
catch (Exception ex)
{
Console.WriteLine(ex.StackTrace);//由于代码24行使用throw ex重置了异常抛出点,所以这里异常堆栈只能捕捉到代码32行和24行抛出的异常,但是13行的异常在堆栈中无法捕捉到
} Console.ReadLine();
}
}
}

可以看到使用throw ex会使得C#重置代码中异常的抛出点,从而让C#认为异常的原始抛出点应该是在代码24行,在异常堆栈中无法捕捉到代码13行所抛出的异常。

throw

使用throw和throw ex唯一的不同就是throw并不会让C#重置异常的抛出点,我们将上面代码中24行的throw ex改为throw如下:

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace ExceptionTesting
{
class Program
{
static void InnerException()
{
throw new Exception("模拟异常");
} static void OuterException()
{
try
{
InnerException();
}
catch(Exception ex)
{
throw;
}
} static void Main(string[] args)
{
try
{
OuterException();
}
catch(Exception ex)
{
Console.WriteLine(ex.StackTrace);//由于现在代码24行使用了throw抛出捕获到的异常,并没有重置原始异常的抛出点,所以这里异常堆栈不但能捕捉到代码32行和24行抛出的异常,还能捕捉到代码13行抛出的异常。
} Console.ReadLine();
}
}
}

由于这一次我们使用了throw来抛出代码24行中catch代码块中捕获到的异常,并没有重置异常的抛出点,因此在上面代码36行这一次异常堆栈输出了13行、24行、32行三个异常。

C#中try catch中throw ex和throw方式抛出异常有何不同的更多相关文章

  1. C#中try catch中throw ex和throw方式抛出异常有何不同_异常捕获堆栈丢失问题

    前言,最近遇到一个使用try-catch异常捕获后记录一下日志,然后再抛出该异常后,异常堆栈里无法显示准确的堆栈地址的问题?   其实以前也遇到过类似问题,没有重视,这次好好研究了下,并上度娘上找了找 ...

  2. C++中Try Catch中的继承

    1.C++中Try Catch简介:我们编译运行程序出错的时候,编译器就会抛出异常.抛出异常要比终止程序灵活许多. 而C++异常是指在程序运行时发生的反常行为,这些行为超出了函数正常功能的范围.当程序 ...

  3. c++中try catch的用法

    c++中try catch的用法 标签: c++exception数据库sqlc 2011-10-24 21:49 45622人阅读 评论(3) 收藏 举报  分类: 一点小结(267)  版权声明: ...

  4. C#:在catch中return,会执行finally吗?

    本文转自 vipxiaotian(CSDN) 请参考下面一段简单的语句块: 1:  try2:  {3:      throw new Exception("new exception&qu ...

  5. C#中try catch finally的执行顺序

    1.首先明确一点,就是不管怎样,finally一定会执行,即使程序有异常,并且在catch中thorw 了 ,finally还是会被执行. 2.当try和catch中有return时,finally仍 ...

  6. javascript中 try catch用法

    javascript中 try catch用法 投稿:hebedich 字体:[增加 减小] 类型:转载 时间:2015-08-16我要评论 JS try catch语句一般在什么情况下使用?是必须使 ...

  7. Java中try catch finally的执行顺序问题

    finally 语句块是在 try 或者 catch 中的 return 语句之前执行的.更加一般的说法是,finally 语句块应该是在控制转移语句之前执行,控制转移语句除了 return 外,还有 ...

  8. java中try{}catch{}和finally{}的执行顺序问题

     今天我给大家讲解一下java的的错误和异常处理机制以及相关异常的执行顺序问题.如有不足的地方,欢迎批评指正~ 1.首相简单介绍一下java中的错误(Error)和异常(Exception) 错误和异 ...

  9. Java中try,catch,finally的用法

    Java中try,catch,finally的用法,以前感觉还算熟悉,但看到一篇博文才有更深点的理解,总结网友博客如下. Java异常处理的组合方式: 1.try+catch  运行流程:运行到try ...

随机推荐

  1. jsp页面中jstl标签详解

    JSLT标签库,是日常开发经常使用的,也是众多标签中性能最好的.把常用的内容,放在这里备份一份,随用随查.尽量做到不用查,就可以随手就可以写出来.这算是Java程序员的基本功吧,一定要扎实. JSTL ...

  2. 諾基亞定制的Android系統名為 Z Launcher

    N1這款產品似乎沒有諾基亞的傳統風格,搭載Android系統以及酷似iPad mini的外觀,都在向外界傳遞著一個信號:諾基亞在變化.不過,沒有了移動設備部門的諾基亞,仍然心系消費電子市場,N1會是個 ...

  3. 分组统计并计算每组数量sql

    有 字段A 和B比如数据如下A  B1  21  31  4 2  22  3 统计出的sql结果: A   count 1   3 2   2 select a,count(b) from t gr ...

  4. Android中 Bitmap和Drawable相互转换的方法

    1.Drawable->Bitmap Resources res=getResources(); Bitmap bmp=BitmapFactory.decodeResource(res, R.d ...

  5. hadoop2.7下载mirror

    http://mirror.bit.edu.cn/apache/hadoop/common/

  6. webkitTransitionEnd webkitAnimationEnd事件

    在CSS 3中,可以通过使用keyframe样式属性与animation样式属性实现animation动画,使用transition样式属性实现transition动画. 在WebKit引擎的浏览器( ...

  7. asp.net mvc 过滤器

    https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters#ordering 1. 对于应用在 Action 上的多个行为 ...

  8. 原生JS事件绑定方法以及jQuery绑定事件方法bind、live、on、delegate的区别

    一.原生JS事件绑定方法: 1.通过HTML属性进行事件处理函数的绑定如: <a href="#" onclick="f()"> 2.通过JavaS ...

  9. 【leedcode】 Longest Palindromic Substring

    Given a , and there exists one unique longest palindromic substring. https://leetcode.com/problems/l ...

  10. bash小技巧

    Linux 下shell基本上默认是 bash, 下面是我总结的一些技巧. &  后台运行程序 ,注意退出当前shell后 程序也会退出()   使用子shell, 比如 (cd ../../ ...