一、问题引入
维护老项目,看到下面一个函数:
/// <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)。从原理上讲的已经很透彻了。