開發軟件過程中,為了防止程序崩潰出錯都會用 try ... catch 包裹住會產生異常的代碼。隨著項目越來越大,try ... catch 語句到處都是,令人眼花的花括號,丑陋無比的縮進讓人崩潰。
IO 操作通常最容易引發異常,文件讀寫網絡訪問數據庫操作這些,比如讀取當前目錄的文本:
string text = System.IO.File.ReadAllText("demo.txt");
Console.WriteLine(text);
IO 異常
這時就需要在外層用 try ... catch 將這兩行代碼包括起來,并提示錯誤:
try
{
string text = System.IO.File.ReadAllText("demo.txt");
Console.WriteLine(text);
}
catch (System.IO.IOException ex)
{
Console.WriteLine(ex.Message);
}
為了簡化上面的代碼結構,在這里我們可以設計一個靜態方法,將需要執行的代碼作為參數,在 try ... catch 里執行它。如何將要執行的代碼作為參數傳給另外一個方法呢,我們可以用 Action<T> 和 Fun<T> 委托,他們一個是無返回參數,一個是有返回參數。
public static void Try(Action act, Action<Exception> errorCallback = null)
{
try
{
act();
}
catch (Exception ex)
{
errorCallback?.Invoke(ex);
}
}
這個 Try 方法接受兩個委托作為參數,第一個是要執行的代碼,第二個是出現了異常也好返回一個 Exception 類型給調用方,并且它是一個可為 null 類型的參數,也就是可選參數。
把這個方法放到一個靜態類 TryCatch 里,在調用的類使用 using 語句使用這個靜態類,沒錯,C# 6 就是這么叼。
using static TryCatch;
改造后的語句:
string text = "";
Try(() =>
{
text = System.IO.File.ReadAllText("demo.txt");
}, error =>
{
WriteLine(error.Message);
});
這里就很厲害了,代碼竟然沒有變短反而變長了和更難理解了。
繼續改造,使用 Func<T>:
public static T Try<T>(Func<T> func, Func<Exception, T> errorCallback = null)
{
try
{
return func();
}
catch (Exception ex)
{
if (errorCallback != null)
{
return errorCallback(ex);
}
return default(T);
}
}
再繼續改造剛才的代碼:
string text = Try(()=> { return System.IO.File.ReadAllText("demo.txt"); });
WriteLine(text);
震驚!某知名函數竟然不報錯,原因竟然是這:
return default(T);
很好理解,就是返回默認值了。
可以在那兩個靜態類里加入 logger,這樣每一個 try ... catch 報錯都可以記錄下來,同理還可以對實現了 IDispose 接口的類封裝,省略 using 代碼塊。