感悟:對泛型委托基本屬於有點認識,但從來沒真正在項目中使用過,有時感覺沒有合適的場景應用,但看了artech兄的文章,我才明白,原來泛型委托真的可以做很多事情,而且效果往往是沒有使用委托所達不到的。
Action<T> 泛型委托:封裝一個方法,該方法只采用一個參數並且不返回值。可以使用此委托以參數形式傳遞方法,而不用顯式聲明自定義的委托。該方法必須與此委托定義的方法簽名相對應。也就是說,封裝的方法必須具有一個通過值傳遞給它的參數,並且不能返回值。當然泛型委托不只是只能支持一個參數,它最多可以支持四個參數。
泛型委托與直接顯示聲明自定義委托的示例比較:
1:顯示聲明自定義委托:
delegate void DisplayMessage(string message);
public class TestCustomDelegate
{
public static void Main()
{
DisplayMessage messageTarget;
messageTarget = ShowWindowsMessage;
messageTarget("Hello, World!");
}
private static void ShowWindowsMessage(string message)
{
MessageBox.Show(message);
}
}
2: Action<T> 用法。比起自定義委托,明顯可以看出代碼簡潔了。
public class TestAction1
{
public static void Main()
{
Action<string> messageTarget;
messageTarget = ShowWindowsMessage;
messageTarget("Hello, World!");
}
private static void ShowWindowsMessage(string message)
{
MessageBox.Show(message);
}
}
Func<T, TResult> 委托:封裝一個具有一個參數並返回 TResult 參數指定的類型值的方法。同理,這裡的泛型委托只是接受一個參數的委托,它最多同樣支持四個參數。TResult:此委托封裝的方法的返回值類型。
問題:目前本公司在寫程序時,都使用了log4net,我想大家在做異常時,都會利用try catch來捕獲異常,日志就在catch塊中完成,但每個方法都寫一堆的try catch往往顯的有點別扭。雖然寫程序時提倡盡量去捕獲具體的錯誤異常,但總會有你預想不到的異常拋出,為此直接捕獲Exception算是不錯的做法。
具體場景:在客戶端調用WCF服務時,我們都需要在客戶做異常處理,最常見的錯誤異常為CommunicationException,TimeoutException,Exception示例如下:
try
{
//執行方法調用
......
(proxy as ICommunicationObject).Close();
}
catch (CommunicationException ex)
{
(proxy as ICommunicationObject).Abort();
WebLog.SquareLog.CommonLogger.Error("取積分廣場首頁酒店數據異常CommunicationException:" + ex.ToString());
}
catch (TimeoutException ex)
{
(proxy as ICommunicationObject).Abort();
WebLog.SquareLog.CommonLogger.Error("取積分廣場首頁酒店數據超時TimeoutException:" + ex.ToString());
}
catch (Exception ex)
{
(proxy as ICommunicationObject).Close();
WebLog.SquareLog.CommonLogger.Error("取積分廣場首頁酒店數據異常Exception:" + ex.ToString());
}
但如果這種代碼遍布整個項目,我想就有重構的必要了,因為項目中最好不要出現類似復制的代碼出現,為此我們可以采用Invoke形式來重構我們已有代碼,下面給出兩個方法,一個是沒有返回值的,一個是有值的。
public static void Invoke<TContract>(TContract proxy, Action<TContract> action)
{
try
{
action(proxy);
(proxy as ICommunicationObject).Close();
}
catch (CommunicationException ex)
{
(proxy as ICommunicationObject).Abort();
WebLog.SquareLog.CommonLogger.Error("取積分廣場首頁酒店數據異常CommunicationException:" + ex.ToString());
}
catch (TimeoutException ex)
{
(proxy as ICommunicationObject).Abort();
WebLog.SquareLog.CommonLogger.Error("取積分廣場首頁酒店數據超時TimeoutException:" + ex.ToString());
}
catch (Exception ex)
{
(proxy as ICommunicationObject).Close();
WebLog.SquareLog.CommonLogger.Error("取積分廣場首頁酒店數據異常Exception:" + ex.ToString());
}
}
public static TReturn Invoke<TContract, TReturn>(TContract proxy, Func<TContract, TReturn> func)
{
TReturn returnValue = default(TReturn);
try
{
returnValue = func(proxy);
}
catch (CommunicationException ex)
{
(proxy as ICommunicationObject).Abort();
WebLog.SquareLog.CommonLogger.Error("取積分廣場首頁酒店數據異常CommunicationException:" + ex.ToString());
}
catch (TimeoutException ex)
{
(proxy as ICommunicationObject).Abort();
WebLog.SquareLog.CommonLogger.Error("取積分廣場首頁酒店數據超時TimeoutException:" + ex.ToString());
}
catch (Exception ex)
{
WebLog.SquareLog.CommonLogger.Error("取積分廣場首頁酒店數據異常Exception:" + ex.ToString());
}
return returnValue;
}
如何調用:可以看出客戶端代碼已經變成一條簡潔代碼了,它即完成了完整的異常處理,而且也把所有能夠捕獲的異常信息記錄下來。
list = ErrorHandler.Invoke<ISearchHotelForSquare, List<HotelGenericInfo>>(cli, proxy => proxy.GetHotelGenericListForSquare(requestInfo).ToList());
說明:至於Invoke的應用,在System .Runtime .Remoting.Proxies有一個RealProxy,可以稱做真正代理,裡面有IMessage Invoke(System.Runtime.Remoting.Messaging.IMessage msg)方法:只不過這裡面沒有應用泛型委托。
// 返回結果:
// The message returned by the invoked method, containing the return value and
// any out or ref parameters.
public abstract IMessage Invoke(IMessage msg);