委託(delegate)和事件(event)

委託

C#的方法有重載(overload)的特性,可以在同一個方法名稱底下有不同的參數數目以及類型。 那麼如果參數一樣, 但有不同的方法名稱, 應該怎麼實現? 就是委託


 static void Main(string[] args)
 {
  int x = 1;
  int y = 2;
  Calculate(x, y, Method.Add);
  Console.ReadKey();
 }
 static int Calculate(int x,int y, Method method)
 {
  switch (method)
   {
    case Method.Add:
     return x + y;
    case Method.Multiply:
     return x * y;
    default:
     return -1;
   }
 }
 enum Method
 {
  Add,
  Multiply,
 }

以上面程式為例, 想要寫一個x,y的運算工具, 但因為運算方法不只加法跟乘法, 未來如果需要擴充的時候, 需要新增 enum的類別、修改switch的結構, 對於越來越多的算法, 程式維護性就越來越低。既然參數都是x跟y, 可以考慮使用委託解決。

首先定義好委託的參數類型以及方法名稱

delegate int Calculate(int x, int y);
//創造了一個Calculate的委託, 具備返回值以及x , y參數。

接下來把原本的加法跟乘法, 單獨分開來成兩個方法(參數需要和委託的一樣)

 static int Add(int x, int y)
 {
  return x + y;
 }
 static int Multiply(int x, int y)
 {
  return x * y;
 }

主程式做一些修改, 首先新增一個委託類型的變數calc, 由於委託可以將方法做為參考傳遞, 變數calc的值可以直接指定滿足條件的方法(例如Add或Multiply), 執行的時候呼叫Invoke即可。 因為將方法做為參考進行傳遞, 未來擴充新計算方法的時候只需要添加新的方法而不需要修改到原有的結構, 大幅降低維護性。


 static void Main(string[] args)
 {
  int x = 1;
  int y = 2;
  Calculate calc = Add;
  calc.Invoke(x, y);
  Console.ReadKey();
 }

calc除了可以指定方法外, 也可以使用"+="新增多個方法,當執行Invoke的時候, 會依序執行註冊的多個方法, 如下

 Calculate calc = Add;
 calc += Multiply;
 calc.Invoke(x, y);

當要刪除註冊的方法時, 使用"-="即可。完整程式碼如下

 delegate int Calculate(int x, int y);
 static void Main(string[] args)
 {
  int x = 1;
  int y = 2;
  Calculate calc = Add;
  calc += Multiply;
  calc.Invoke(x, y);
  Console.ReadKey(); 
 }
 static int Add(int x, int y)
 {
  return x + y;
 }
 static int Multiply(int x, int y)
 {
  return x * y;
 }

從程式碼可以發現, Calculator calc=Add, 在第一次使用的時候需要賦值。 Calculator這個委託很有可能被其他的物件不小心修改, 造成物件本身執行的錯誤, 這與物件導向的資料封裝Encapsulation會有所出入, 這時候我們可以使用event事件。


事件

事件是架構在委託的基礎之上, 不同的是, 使用事件的時候只能用"+="或"-="來添加或刪減, 不能用"="來賦值, 確保了物件本身的資料封裝安全性。同樣的程式, 使用事件後的程式碼如下

除了定義好委託的類型Calculate之外, 在定義一個使用Calculate作為委託類型的事件StartEvent, 在程式法中只能透過"+="添加事件, 有效保護物件的資料。


 delegate int Calculate(int x, int y);
 static event Calculate StartEvent;
 
 static void Main(string[] args)
 {
  int x = 1;
  int y = 2;
  StartEvent += Add;
  StartEvent += Multiply;
  StartEvent.Invoke(x, y);
  Console.ReadKey(); 
 }
 
 
 static int Add(int x, int y)
 {
  return x + y;
 }
 static int Multiply(int x, int y)
 {
  return x * y;
 }
 

同樣的概念, C#提供了一個EventHandler的委託類型

public delegate void EventHandler(object sender, EventArgs e);

https://docs.microsoft.com/zh-tw/dotnet/api/system.eventhandler?view=netframework-4.0

sender的類型是物件類型, 可以將產生事件的物件傳遞出來

EventArgs是抽象類別, 用戶可以繼承後創造自己的事件類別, 將需要的資訊傳遞出來

public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e) where TEventArgs : EventArgs;

需要特別提到, 委託是將方法做為參考傳遞, 因此實際上執行方法的執行緒, 是在調用invoke方法的執行緒上。使用時機上需要留意跨執行緒的錯誤問題(後續章節會討論)

0 次瀏覽

©JYTEK Taiwan 2019