【所见即所得】Try Catch 效率问题
一、问题引入
维护老项目,看到下面一个函数:
/// <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的性能问题,基本上已经有定论。自己想验证下:
测试的类代码如下:
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!"; } } }
主调函数如下:
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)。从原理上讲的已经很透彻了。
作者:付之一笑
出处:博客园——付之一笑
希望对您有帮助,您的关注和推荐是对我最大的支持。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。