【所见即所得】Try Catch 效率问题
一、问题引入
维护老项目,看到下面一个函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | /// <summary> /// 从ViewState中获取某个属性的值。如果该属性不存在,返回空字符串。 /// </summary> /// <param name="PropertyName">属性名称</param> /// <returns>属性值(属性不存在时返回空字符串)</returns> protected string GetViewState( string PropertyName) { try { return ViewState[PropertyName].ToString(); } catch (NullReferenceException) { return "" ; } } |
二、问题分析
代码功能很明显“从ViewState中获取某个属性的值。如果该属性不存在,返回空字符串。”。看起来也很整洁,想起以前在园子类看到的讨论try-catch的性能问题,基本上已经有定论。自己想验证下:
测试的类代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | public class TryCatch { static Stopwatch sw = new Stopwatch(); /// <summary> /// 带有finally的try引发异常来完成字符串赋值 /// </summary> /// <param name="str">传入参数,调用的时候会是null</param> /// <returns></returns> public static string TryCatchwithFinally( string str) { sw.Reset(); sw.Start(); try { return str.ToString(); } catch (NullReferenceException) { return "exception!" ; } finally { sw.Stop(); Console.WriteLine( "发生exception,带finally的trycatch用时{0}毫秒" ,sw.ElapsedMilliseconds); } } /// <summary> /// 带有finally的try不引发异常通过ifelse来完成字符串赋值 /// </summary> /// <param name="str">传入参数,调用的时候会是null</param> /// <returns></returns> public static string IfElsewithFinally( string str) { sw.Reset(); sw.Start(); try { if (! string .IsNullOrEmpty(str)) { return str.ToString(); } else { return "" ; } } catch (NullReferenceException) { return "exception!" ; } finally { sw.Stop(); Console.WriteLine( "不发生exception,带finally得ifelse用时{0}毫秒" , sw.ElapsedMilliseconds); } } /// <summary> /// 不带有finally的try不引发异常通过ifelse来完成字符串赋值 /// </summary> /// <param name="str">传入参数,调用的时候会是null</param> /// <returns></returns> public static string IfElse( string str) { sw.Reset(); sw.Start(); try { if (! string .IsNullOrEmpty(str)) { return str.ToString(); } else { sw.Stop(); Console.WriteLine( "发生exception,ifelse用时{0}毫秒" , sw.ElapsedMilliseconds); return "" ; } } catch (NullReferenceException) { return "exception!" ; } } /// <summary> /// 不带有finally的try引发异常来完成字符串赋值 /// </summary> /// <param name="str">传入参数,调用的时候会是null</param> /// <returns></returns> public static string Trycatch( string str) { sw.Reset(); sw.Start(); try { return str.ToString(); } catch (NullReferenceException) { sw.Stop(); Console.WriteLine( "发生exception,trycatch用时{0}毫秒" , sw.ElapsedMilliseconds); return "exception!" ; } } } |
主调函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using FMS_Refacting.try_catch; namespace Fibo.ConsoleApplication { class Program { static void Main( string [] args) { TryCatch.IfElsewithFinally( null ); //1 TryCatch.Trycatch( null ); //2 TryCatch.TryCatchwithFinally( null ); //3 TryCatch.IfElse( null ); //3 Console.ReadKey(); } } } |
结果如下:
将主调函数main中4个语句调换顺序,得到的测试结果大体相同。也就是,用trycatch实现逻辑(简单的)耗时在100毫秒(这个有可能不太准确,不过大约在100倍左右)以上,而ifelse基本不耗时。同一类型的异常被第一次抛出的时候性能会下降很多,而在后续抛出则基本没有影响,CLR在异常处理的这一点进行了强大的优化。
同时finally中如果没有什么复杂的逻辑的话并不会影响性能。
下面是 imfunny兄一些高见:
这个结论貌似不对的。
和复杂的逻辑其实没有什么关系。所以楼主改正下呢。
缺少了finally 实际上就缺少了ret的分界表,于是就导致了对异常类型以及异常类型的匹配过程。而这个部分才是耗时的。
finally 即使有复杂的逻辑也没有关系。这个其实是代码执行的时间。比如有些对资源进行回收导致的代的回收等。
实际上catch过程可用可不用。如果不用可以再Application扑捉到嘿嘿。
当然为了美观和可读性,ifelse可以用?:和??来代替。
三、所得
对trycatch的运用;只控制异常,不控制逻辑。
坚信:过度的设计是犯罪,不设计更是犯罪!
知道了,并运用了Stopwatch类;
以后继续所见即所得的学习!
PS: 在园子里面找到了金大侠的大作:深入理解CLR异常处理机制(http://www.cnblogs.com/bitfan/archive/2009/12/03/1616550.html)。从原理上讲的已经很透彻了。
作者:付之一笑
出处:博客园——付之一笑
希望对您有帮助,您的关注和推荐是对我最大的支持。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步