委派的疊加組合(多點傳送委派)

在C#中我們可以透過委派的疊加組合來實現多點傳送委派,這樣可以讓一次性的委派呼叫,即可調用到多個方法。多點傳送委派其實適用的場景並沒有很多,大多屬於資料流處理的後段(也就是不再運算,沒有回傳值,準備下車了),例如:日誌記錄、事件通知、異常處理、或者是輸出列印等應用。

C#委派的疊加組合

在C#中,委派的疊加組合是通過+=-=運算符來實現的。例如我們有一個應用情境,當我們在購買東西結帳後,需要針對這一次的購物進行一些單據的列印,通常都是列印出發票刷卡單據,這兩個單據的資訊大致上是相同的,只是細節略有不同。然而消費者可以透過店員的詢問是否要排除某些列印的單據,這會讓後面的情況變得有點雜亂。(當然啦,也沒有多混亂!這裡只是舉出一個簡單的例子,來套用多點傳送委派的應用。)

System.Action<int> printInvoice = x => { Console.WriteLine($"發票票券:金額{x:n0}元"); };
System.Action<int> printReceipt = x => { Console.WriteLine($"收據票券:金額{x:n0}元"); };

var delePrintAll = printInvoice + printReceipt;

var iMoney = 1234;

Console.Write($@"
0. 全部列印(預設)
1. 不印發票
2. 不印收據
請選擇列印模式?");
      
var deleRun = System.Convert.ToInt32(Console.ReadLine()) switch
{
  1 => delePrintAll -= printInvoice,
  2 => delePrintAll -= printReceipt,
  _ => delePrintAll,
};

Console.WriteLine("-----\n您拿到的票券:");
deleRun?.Invoke(iMoney);

透過上面的程式碼我們可以發現我們創造了System.Action<int>型別的委派分別為printInvoiceprintReceipt,而delePrintAll則是將這兩個委派進行疊加組合。最後透過deleRun來決定要執行的委派,這樣就可以達到多點傳送委派的效果。

最後要說明的是deleRun?.Invoke是為了調用委派前先檢查是否為null,這樣可以避免在沒有委派的情況下造成例外的發生。至於你可能會想說哪有可能是null呢?其實這是為了避免委派被移除的情況下造成的例外發生,例如:delePrintAll = delePrintAll - printInvoice - printReceipt

結語與補充

多點傳送委派的應用場景並不多,但是在某些情況下,這樣的設計可以讓程式碼更加的簡潔與易讀。如果你試圖使用多點傳送委派來解決複合性的計算問題(例如某個委派計算後拋值再丟到另外一個委派運算,那麼光利用+=-=運算子是行不通的,例如示範一個先將兩數相加後再乘以尾數的委派:

System.Func<(int, int), int> add = (x) => x.Item1 + x.Item2;
System.Func<(int, int), int> sub = (x) => x.Item1 - x.Item2;
System.Func<(int, int), int> addThenMul = (x) => add(x) * x.Item2;

System.Console.WriteLine(addThenMul((3, 4)));

你唯一能做的事就是另外創造addThenMul的委派然後在裡面呼叫addsub方法,除此之外我沒有想到更好的解法,話說回來這樣的寫法跟一般的C#函數方法撰寫方式根本沒啥兩樣了。

相關連結

C# Delegate AnonymousFunctions LambdaExpressions GeneralDelegates Action Func CombineDdelegates MulticastDelegates