委託app
委託與事件,常常拿來就用,每每忘記其實現原理,對其使用方式也愈來愈侷限。周家安老師在《C# 6.0 學習筆記》中對委託和事件的講解,深刻淺出,清晰明瞭,故特地摘抄一篇隨筆,勤看勤思。學習
首先,委託是一種形式上與方法簽名類似的類型。this
定義一個委託:spa
public delegate void DoSome(string msg);
使用關鍵字 delegate, 類型名稱爲 DoSome(string msg).code
建立一個DoSome(string msg)委託類型對象,並實例化。對象
1 static void TestDo(string str) 2 { 3 // ToDo 4 } 5 6 7 DoSome d1 = new DoSome(TestDo); 8 9 10 // 或者 11 12 DoSome d2; 13 14 d2 = TestDo;
委託列表blog
1 public delegate void DoSome(string msg); 2 static void Main(string[] args) 3 { 4 DoSome d = new DoSome(TestDo); 5 d("Hello"); 6 Console.WriteLine("--------------------------------------"); 7 d += new DoSome(Test1); 8 d("123"); 9 Console.WriteLine("--------------------------------------"); 10 d += TDo2; 11 d("world"); 12 Console.WriteLine("--------------------------------------"); 13 d -= TestDo; 14 d("nihao"); 15 } 16 17 static void TestDo(string str) 18 { 19 Console.WriteLine(str); 20 } 21 22 static void Test1(string str) 23 { 24 Console.WriteLine("Test1 + " + str); 25 } 26 static void TDo2(string str) 27 { 28 Console.WriteLine("TDo2 + " + str); 29 }
輸出:接口
事件事件
事件自身就是委託類型。get
1 class MyApp 2 { 3 public delegate void SpaceKeyPressedEventHandler(); 4 5 // 聲明事件 6 public event SpaceKeyPressedEventHandler SpaceKeyPressed; 7 8 // 經過該方法引起事件 9 protected virtual void OnSpaceKeyPressed() 10 { 11 12 if (this.SpaceKeyPressed != null) 13 { 14 // 將事件分發 15 SpaceKeyPressed(); 16 } 17 } 18 19 // 啓動事件監聽的接口 20 public void StartRun() 21 { 22 // 監聽事件 23 while (true) 24 { 25 ConsoleKeyInfo keyinfo = Console.ReadKey(); 26 if (keyinfo.Key == ConsoleKey.Spacebar) 27 { 28 // 引起事件 29 OnSpaceKeyPressed(); 30 } 31 32 if (keyinfo.Key == ConsoleKey.Escape) 33 { 34 // 跳出循環 35 break; 36 } 37 } 38 } 39 } 40 41 class Program 42 { 43 44 static void Main(string[] args) 45 { 46 MyApp app = new MyApp(); 47 // 訂閱事件,指定處理事件的方法 48 app.SpaceKeyPressed += app_SpaceKeyPressed; 49 app.SpaceKeyPressed += app_SecondEventHandler; 50 51 // 啓動事件監聽 52 app.StartRun(); 53 54 } 55 56 // 事件處理1 57 private static void app_SpaceKeyPressed() 58 { 59 Console.WriteLine("{0} 按下空格鍵。", DateTime.Now.ToLongTimeString()); 60 } 61 // 事件處理2 62 private static void app_SecondEventHandler() 63 { 64 Console.WriteLine("事件的第二個處理方法。"); 65 } 66 67 }
一般,做爲事件委託,有兩個參數,一個是Object類型,表示引起事件的對象,便是誰引起了事件的,多數狀況下在調用事件時是把類的當前實例引用(this)傳遞過去。另外一個參數是從System.EventArgs派省的類的實例。這是一個標準的事件處理程序的簽名,爲了規範事件的處理,.NET類庫已經定義好一個System.EventHandler委託,用於聲明事件。它的原型以下:
1 public delegate void EventHandler(object sender, EventArgs e);
引起事件的對象實例將傳遞給sender參數,而與事件相關的數據則傳遞給e參數。若是不須要傳遞過多的數據,能夠經過System.EventArgs.Empty靜態成員返回一個空的EventArgs對象類傳遞。
可是,因爲不一樣的事件要傳遞的參數不一樣,更多時候是從EventArgs類派生的子類的實例,顯然一個EventHandler委託是不能知足各類狀況的。若是針對不一樣的事件也頂一個一個對應的委託,水量一旦多起來,既混亂,也很差管理。爲了解決這個問題,.NET類庫又提供了一個帶有泛型參數的事件處理委託。原型以下:
1 public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
TEventArgs 是一個泛型參數,應該是System.EventArgs類或者System.EventArgs類的派生類型。
泛型參數的事件,實例:
1 // EventArgs 派生類 2 // 建立泛型參數 KeyPressedEventArgs 類 3 public class KeyPressedEventArgs : EventArgs 4 { 5 public ConsoleKey pressedKey { get; private set; } 6 public KeyPressedEventArgs(ConsoleKey key) 7 { 8 pressedKey = key; 9 } 10 } 11 12 public class MyApp 13 { 14 // 捕捉按鍵的事件 聲明一個泛型參數KeyPressedEventArgs類型的 15 public event EventHandler<KeyPressedEventArgs> KeyPressed; 16 17 // 經過該方法引起事件 18 protected virtual void OnKeyPressed(KeyPressedEventArgs e) 19 { 20 if (this.KeyPressed != null ) 21 { 22 this.KeyPressed(this, e); 23 } 24 } 25 26 // 事件監聽端口啓動 27 public void Start() 28 { 29 while (true) 30 { 31 ConsoleKeyInfo keyInfo = Console.ReadKey(); 32 // 若是按下了ESC鍵,則退出循環 33 if (keyInfo.Key == ConsoleKey.Escape) 34 { 35 break; 36 } 37 // 引起事件 38 OnKeyPressed(new KeyPressedEventArgs(keyInfo.Key)); 39 } 40 } 41 } 42 43 44 class Program 45 { 46 47 static void Main(string[] args) 48 { 49 50 MyApp app = new MyApp(); 51 // 訂閱事件,指定處理事件的方法 52 app.KeyPressed += app_KeyPressed; 53 // 啓動事件監聽 54 app.Start(); 55 } 56 57 // 事件處理 58 private static void app_KeyPressed(Object sender, KeyPressedEventArgs e) 59 { 60 Console.WriteLine("已按下 {0} 鍵", e.pressedKey.ToString()); 61 } 62 63 }