Action<T>、Func<T, TResult> 等泛型委派是 .NET 2.0 引入的強大工具,能以類型安全方式傳遞程式碼塊,避免重複撰寫委派類型。本文展示如何利用泛型委派重構遍佈專案的 WCF 客戶端異常處理樣板碼,實現「一次定義,處處復用」。
呼叫 WCF 服務時,必須處理 CommunicationException、TimeoutException 並正確關閉/中止通道:
try
{
// 呼叫服務
var result = proxy.GetData(request);
(proxy as ICommunicationObject).Close();
}
catch (CommunicationException ex)
{
(proxy as ICommunicationObject).Abort();
Logger.Error("CommunicationException: " + ex);
}
catch (TimeoutException ex)
{
(proxy as ICommunicationObject).Abort();
Logger.Error("TimeoutException: " + ex);
}
catch (Exception ex)
{
(proxy as ICommunicationObject).Close();
Logger.Error("Exception: " + ex);
}
專案中有數十個服務呼叫點,複製貼上導致維護噩夢。
定義兩個靜態輔助方法,分別處理無回傳值與有回傳值的服務呼叫:
public static class WcfInvokeHelper
{
/// 無回傳值服務呼叫
public static void Invoke<TContract>(TContract proxy, Action<TContract> action)
where TContract : class
{
try
{
action(proxy);
(proxy as ICommunicationObject).Close();
}
catch (CommunicationException ex)
{
(proxy as ICommunicationObject).Abort();
LogError("CommunicationException", ex);
}
catch (TimeoutException ex)
{
(proxy as ICommunicationObject).Abort();
LogError("TimeoutException", ex);
}
catch (Exception ex)
{
(proxy as ICommunicationObject).Close();
LogError("Exception", ex);
}
}
/// 有回傳值服務呼叫
public static TReturn Invoke<TContract, TReturn>(TContract proxy, Func<TContract, TReturn> func)
where TContract : class
{
TReturn result = default;
try
{
result = func(proxy);
(proxy as ICommunicationObject).Close();
}
catch (CommunicationException ex)
{
(proxy as ICommunicationObject).Abort();
LogError("CommunicationException", ex);
}
catch (TimeoutException ex)
{
(proxy as ICommunicationObject).Abort();
LogError("TimeoutException", ex);
}
catch (Exception ex)
{
(proxy as ICommunicationObject).Close();
LogError("Exception", ex);
}
return result;
}
private static void LogError(string prefix, Exception ex)
{
// 實際專案使用 log4net/NLog 等
Console.WriteLine($"[{prefix}] {ex}");
}
}
無回傳值:
WcfInvokeHelper.Invoke<IOrderService>(proxy, p => p.SubmitOrder(orderDto));
有回傳值:
var hotels = WcfInvokeHelper.Invoke<IHotelSearch, List<HotelInfo>>(
proxy, p => p.SearchHotels(criteria).ToList());
Lambda 運算式直接內嵌業務邏輯,異常處理、通道管理完全內聚於輔助方法。
// 傳統:每種簽名都要聲明委派類型
delegate void DisplayMessage(string msg);
delegate TResult Calculate<T1, T2, TResult>(T1 a, T2 b);
// 泛型委派:框架內建,開箱即用
Action<string> act = Console.WriteLine;
Func<int, int, int> add = (a, b) => a + b;
Action<T1...T4> 對應無回傳值方法,Func<T1...T4, TResult> 對應有回傳值方法,最多支援 16 個參數(.NET 4.0+),完全覆蓋實際需求。
.NET Remoting 的 RealProxy 核心方法 IMessage Invoke(IMessage msg) 同樣是攔截呼叫的統一入口,但缺乏泛型委派的類型安全與語法便利。現代專案推薦使用 Castle DynamicProxy、PostSharp 或 .NET Core 內建 DispatchProxy 實現 AOP,配合泛型委派處理橫切關注點(日誌、交易、重試、快取)。
泛型委派不是高深理論,而是消除樣板碼、提升程式碼表達力的利器。下次看到重複的 try-catch、using、鎖定、重試邏輯,請嘗試用 Action/Func 封裝成可復用的輔助方法——程式碼會變得更乾淨、更安全、更易維護。