一、問題引入
維護老項目,看到下面一個函數:
/// <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)。從原理上講的已經很透徹了。

