C#中Try-Catch语句真的影响程序性能吗?

 更新时间:2015年06月05日 09:58:23   投稿:junjie  
这篇文章主要介绍了C#中Try-Catch语句真的影响程序性能吗?本文结合IL分析Try-Catch语句的性能问题,需要的朋友可以参考下
(福利推荐:【腾讯云】服务器最新限时优惠活动,云服务器1核2G仅99元/年、2核4G仅768元/3年,立即抢购>>>:9i0i.cn/qcloud

(福利推荐:你还在原价购买阿里云服务器?现在阿里云0.8折限时抢购活动来啦!4核8G企业云服务器仅2998元/3年,立即抢购>>>:9i0i.cn/aliyun

很多帖子都分析过Try-Catch的机制,以及其对性能的影响。

但是并没有证据证明,Try-Catch过于损耗了系统的性能,尤其是在托管环境下。记得园子里有位网友使用StopWatch分析过Try-Catch在不同情况下,与无Try-Catch的代码相比,代码运行的时间指标,结果并没有很大差异。

下面我来结合IL分析一下Try-Catch吧。

● 机制分析

 .Net 中基本的异常捕获与处理机制是由try…catch…finally块来完成的,它们分别完成了异常的监测、捕获与处理工作。一个try块可以对应零个或多个catch块,可以对应零个或一个finally块。不过没有catch的try似乎没有什么意义,如果try对应了多个catch,那么监测到异常后,CLR会自上而下搜索catch块的代码,并通过异常过滤器筛选对应的异常,如果没有找到,那么CLR将沿着调用堆栈,向更高层搜索匹配的异常,如果已到堆栈顶部依然没有找到对应的异常,就会抛出未处理的异常了,这时catch块中的代码并不会被执行。所以距离try最近的catch块将最先被遍历到。

如有以下代码:

复制代码 代码如下:

    try
   {
       Convert.ToInt32("Try");
   }
       catch (FormatException ex1)
   {

       string CatchFormatException = "CatchFormatException";
   }
       catch (NullReferenceException ex2)
   {

       string CatchNullReferenceException = "CatchNullReferenceException";
   }

   finally
   {
       string Finally = "Finally";
   }

对应IL如下:

复制代码 代码如下:

.method private hidebysig instance void Form1_Load(object sender,
class [mscorlib]System.EventArgs e) cil managed
{
// Code size 53 (0x35)
.maxstack 1
.locals init ([0] class [mscorlib]System.FormatException ex1,
[1] string CatchFormatException,
[2] class [mscorlib]System.NullReferenceException ex2,
[3] string CatchNullReferenceException,
[4] string Finally)
IL_0000: nop
IL_0001: nop
IL_0002: ldstr "Try"
IL_0007: call int32 [mscorlib]System.Convert::ToInt32(string)
IL_000c: pop
IL_000d: nop
IL_000e: leave.s IL_0026
IL_0010: stloc.0
IL_0011: nop
IL_0012: ldstr "CatchFormatException"
IL_0017: stloc.1
IL_0018: nop
IL_0019: leave.s IL_0026
IL_001b: stloc.2
IL_001c: nop
IL_001d: ldstr "CatchNullReferenceException"
IL_0022: stloc.3
IL_0023: nop
IL_0024: leave.s IL_0026
IL_0026: nop
IL_0027: leave.s IL_0033
IL_0029: nop
IL_002a: ldstr "Finally"
IL_002f: stloc.s Finally
IL_0031: nop
IL_0032: endfinally
IL_0033: nop
IL_0034: ret
IL_0035:
// Exception count 3
.try IL_0001 to IL_0010 catch [mscorlib]System.FormatException handler IL_0010 to IL_001b
.try IL_0001 to IL_0010 catch [mscorlib]System.NullReferenceException handler IL_001b to IL_0026
.try IL_0001 to IL_0029 finally handler IL_0029 to IL_0033
} // end of method Form1::Form1_Load

末尾的几行代码揭示出IL是怎样处理异常处理的。最后三行的每一个Item被称作Exception Handing Clause,EHC组成Exception Handing Table,EHT与正常代码之间由ret返回指令隔开。

    可以看出,FormatException排列在EHT的第一位。

    当代码成功执行或反之而返回后,CLR会遍历EHT:

    1. 如果抛出异常, CLR会根据抛出异常的代码的“地址”找到对应的EHC(IL_0001 to IL_0010为检测代码的范围),这个例子中CLR将找到2条EHC,FormatException会最先被遍历到,且为适合的EHC。

    2. 如果返回的代码地址在IL_0001 to IL_0029内,那么还会执行finally handler 即IL_0029 to IL_0033中的代码,不管是否因成功执行代码而返回。

    事实上,catch与finally的遍历工作是分开进行的,如上文所言,CLR首先做的是遍历catch,当找到合适的catch块后,再遍历与之对应finally;而且这个过程会递归进行至少两次,因为编译器将C#的try…catch…finally翻译成IL中的两层嵌套。

当然如果没有找到对应的catch块,那么CLR会直接执行finally,然后立即中断所有线程。Finally块中的代码肯定会被执行,无论try是否检测到了异常。

改进建议

    由上面的内容可以得出:

    如果使用了“Try-Catch”,且捕获到了异常,CLR做的只不过是遍历Exception Handing Table中的Catch项;然后再次遍历Exception Handing Table中的Finally项,所用时间几乎都花费在遍历Exception Handing Table上;而如果没有捕获到异常,CLR只是遍历Exception Handing Table中的Finally项,所需时间微乎其微。

    而“Try-Catch”遍历后的执行对应操作所用时间,则根据你的具体代码所定,“Try-Catch”引起的只是监控与触发,不应将这部分的代码时间也算“Try-Catch”的消耗。

    所以,可以从性能和代码评审两方面考虑,一般建议有以下几点准则:

    1.尽量给CLR一个明确的异常信息,不要使用Exception去过滤异常

    2.尽量不要将try…catch写在循环中

    3. try尽量少的代码,如果有必要可以使用多个catch块,并且将最有可能抛出的异常类型,书写在距离try最近的位置

    4.不要只声明一个Exception对象,而不去处理它。这样做白白增加了Exception Handing Table的长度。

    5.使用性能计数器实用工具的“CLR Exceptions”检测异常情况,并适当优化

    6.使用成员的Try-Parse模式,如果抛出异常,那么用false代替它

    结论,Try-Catch虽然会消费一点时间,但程序人员大可不必谈虎色变,通过上面的分析,与其说“Try-Catch”会损耗或影响性能,不如说“Try-Catch”与其他代码一样,只是性能的普通消费者,但出于代码书写评审方面的考虑,还是尽量关照一下“Try-Catch”吧。

相关文章

  • 新手小白用C# winform 读取Excel表的实现

    新手小白用C# winform 读取Excel表的实现

    这篇文章主要介绍了新手小白用C# winform 读取Excel表的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-01-01
  • C#实现快速排序算法

    C#实现快速排序算法

    本文详细讲解了C#实现快速排序算法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-04-04
  • C#实现利用Windows API读写INI文件的方法

    C#实现利用Windows API读写INI文件的方法

    这篇文章主要介绍了C#实现利用Windows API读写INI文件的方法,涉及C#针对ini文件的创建、读取及写入等操作技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-07-07
  • C#迭代器方法介绍

    C#迭代器方法介绍

    这篇文章主要介绍了C#迭代器方法,可以使用foreach循环语句进行的迭代的方法,称为可迭代方法,或者迭代器方法,方法操作,想了解更多内容得小伙伴可以学习下面文章内容,希望给你的学习带来帮助
    2022-03-03
  • C#设置右键菜单的方法

    C#设置右键菜单的方法

    这篇文章主要介绍了C#设置右键菜单的方法,实例分析了C#设置右键菜单的相关技巧,需要的朋友可以参考下
    2015-06-06
  • C#中PuppeteerSharp库的应用详解

    C#中PuppeteerSharp库的应用详解

    PuppeteerSharp是一个针对Google Chrome浏览器的高级API库,这篇文章主要为大家详细介绍了PuppeteerSharp库在C#中的具体应用,需要的小伙伴可以了解下
    2024-01-01
  • c#读取excel内容内容示例分享

    c#读取excel内容内容示例分享

    这篇文章主要介绍了c#读取excel内容内容示例,要求Excel需是.xls格式,需要的朋友可以参考下
    2014-03-03
  • C#深拷贝方法探究及性能比较(多种深拷贝)

    C#深拷贝方法探究及性能比较(多种深拷贝)

    这篇文章主要介绍了C#中使用NetCDF存储二维数据的读写操作简单应用,探究了以下几种C#对象深拷贝方式,同时简单对比了以下列出的几种深拷贝方式的速度,需要的朋友可以参考下
    2022-04-04
  • C#集合之队列的用法

    C#集合之队列的用法

    这篇文章介绍了C#集合之队列的用法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-04-04
  • WinForm子窗体访问父窗体控件的实现方法

    WinForm子窗体访问父窗体控件的实现方法

    WinForm子窗体访问父窗体控件的实现方法,需要的朋友可以参考一下
    2013-03-03

最新评论

?


http://www.vxiaotou.com