預備知識html
在學習委託和事件以前,咱們須要知道的是,不少程序都有一個共同的需求,即當一個特定的程序事件發生時,程序的其餘部分能夠獲得該事件已經發生的通知。安全
而發佈者/訂閱者模式能夠知足這種需求。簡單來講,在這種模式中,發佈者定義了一系列程序的其餘部分可能感興趣的事件。其餘類能夠「註冊」,以便再這些事件發生時發佈者能夠通知它們。這些訂閱者類經過向發佈者提供一個方法來「註冊」以獲取通知。當事件發生時,發佈者「觸發事件」,而後執行訂閱者提交的全部事件。框架
注:由訂閱者提供的方法稱爲回調方法,或者說回調函數,關於回調函數能夠參考https://www.zhihu.com/question/1980113 。函數
大概瞭解了下基本機制後,咱們再來看看委託和事件:學習
什麼是委託?this
委託和類同樣,是一種用戶自定義的類型。但類表示的是數據和方法的集合,而委託則持有一個或多個方法,以及一系列預約義操做(你能夠暫時把它理解成一個類型安全的C++函數指針)。能夠經過如下操做來使用委託:spa
1)聲明一個委託類型(相似方法聲明,但委託沒有實現塊)3d
public delegate void MyDel(int x); //格式爲[修飾符] [委託關鍵字delegate] [返回類型][委託名][簽名]
2)使用該委託類型聲明一個委託變量。指針
MyDel delVar;
3)建立委託類型的對象,把它賦值給委託對象。新的委託對象包括指向某個方法的引用,這個方法和第一步的簽名和返回類型一致。rest
建立委託對象能夠這樣:
delVar = new MyDel(SomeClass.Method); //建立委託並保存第一個個方法的引用
或者使用快捷語法,好比這樣:
delVar = SomeClass.Method;
4 )也能夠爲委託進行賦值,但這樣舊的委託引用會被GC回收掉。
delVar = new MyDel(SomeClass.Method1);//建立一個新委託對象 delVar = OtherClass.Method2; //賦值後,以前的Method1的引用被Method2覆蓋掉了
5)爲委託對象增長或移除其餘的方法
//增長方法 delVar += someMethod1; delVar += someMethod2; //移除方法 delVar -= someMethod1;
6)組合委託。能夠將兩個委託組合生成成一個新的委託,這個新的委託的調用列表鏈接了其餘兩個委託的調用列表副本。
MyDel delA = someClass.Method1; MyDel delB = otherClass.Method2; MyDel delC = delA + delB;
7)調用委託。調用委託跟調用函數方法同樣,參數必須同其調用列表中的方法一致。調用委託時會執行它的調用列表中的全部方法。(注:調用時委託不能爲空)
delVar(Parameters);
其餘注意事項:1.調用帶返回值的委託時,委託的返回值爲其調用列表中最後一個方法的返回值。2.調用帶引用參數的委託時,參數會根據調用列表中的一個或多個方法的返回值二改變。
什麼是事件?
一句話歸納,事件就好像是專門用於某種特殊用途的簡單委託的封裝。或者說,事件包含了一個私有的委託(也就是說你沒法直接訪問事件的委託)。
另外,事件中可用的操做比委託要少,對於事件咱們只能夠添加、刪除或調用事件處理程序。事件被觸發時,它調用對應的委託來依次調用調用列表中的方法。
對於事件的使用,.Net框架提供了一種標準模式,它定義了一種標準的委託類型EventHandler。
EventHandler聲明以下:
public delegate void EventHandler(object sender,EventArgs e)
// sender保存的是觸發事件的引用,由於是object,因此能夠匹配任何類型的實例 (可使用as運算符拆箱轉換)
// EventArgs是全部事件信息的基類,你能夠本身聲明一個繼承自EventArgs的事件信息類來保存一些須要傳遞的數據信息。
聲明EventHandler對應的事件:
public event EventHandler someEvent;
注:你能夠按照 delegate void XXXXHandler(object sender,EventArgs e)的格式聲明自定義的標準委託類型。
增長移除事件處理程序和委託相似,這裏就再也不贅述。
說了這麼多,最好親自動手實踐一下,這樣才能鞏固本身學習的知識。
一個小小的應用例子
LOL是如今許多人(包括我)十分喜好的一款很流行的5v5多人遊戲,這個遊戲裏面的一方中5我的按職責劃分爲上單、打野、中單、ADC、輔助。這裏主要是經過打野和上單的一個簡單互動來講明一下事件的觸發機制。具體的代碼以下:
1 using System; 2 using System.Threading; 3 4 namespace EventStudy 5 { 6 7 8 9 public class Top //事件發佈者(上單) 10 { 11 public string Hero { get; set; } //使用的英雄 12 public int hp { get; set; } = 500; //英雄生命值 13 public delegate void LowHphandler(object sender, LowHpEventArgs e); //聲明一個微軟標準類型的委託 14 public event LowHphandler LowHpEvent; //聲明一個該委託類型的事件 15 16 public class LowHpEventArgs : EventArgs //低生命事件信息 17 { 18 public readonly string LowHpHero; 19 public int restHp { get; set; } 20 public LowHpEventArgs(string hero, int _resthp) 21 { 22 LowHpHero = hero; 23 restHp = _resthp; 24 } 25 } 26 27 public void BackToBase() //回城補給 28 { 29 Console.WriteLine("{0}回城了",Hero); 30 hp = 500; 31 Thread.Sleep(3000); 32 Console.WriteLine("他傳送回了上路"); 33 } 34 35 public Top(string hero) //構造函數 36 { 37 Hero = hero; 38 } 39 40 41 public void fight(int battleCounts )//戰鬥 battleCounts = 戰鬥次數 42 { 43 for(int i = 0; i < battleCounts;i++) //戰鬥流程 44 { 45 Console.WriteLine("我方上單{0}正在與對方上單激烈對線中......",Hero); 46 Thread.Sleep(2000); 47 hp -= 100; 48 Console.WriteLine("交戰後剩餘生命值:{0}", hp); 49 Thread.Sleep(1000); 50 if(hp <= 100) //當生命值小於等於100時 51 { 52 if (LowHpEvent != null) //若是有對象註冊 53 LowHpEvent(this, new LowHpEventArgs(Hero, hp)); //觸發事件 54 else 55 Console.WriteLine("{0}說:草,沒人關注上路啊?",Hero); 56 break; 57 } 58 } 59 } 60 61 } 62 63 class Jungle //訂閱者(打野) 64 { 65 public string HeroName; //使用的英雄名字 66 public Jungle(string name)//構造函數 67 { 68 HeroName = name; 69 } 70 public void Help(object sender,Top.LowHpEventArgs e)//回調函數,或者說事件處理程序 71 { 72 Console.WriteLine("{0}說:臥槽,咱們的上單{1}只有:{2}血了,我得去幫他",HeroName, e.LowHpHero, e.restHp); 73 } 74 } 75 76 77 78 class Program 79 { 80 static void Main(string[] args) 81 { 82 //建立一個上單和一個打野 83 Top Yassuo = new Top("疾風劍豪-亞索"); 84 Jungle Leesin = new Jungle("盲僧-李青"); 85 86 Yassuo.LowHpEvent += Leesin.Help; //李青開始關注着亞索的生命狀況 87 Yassuo.fight(4); //亞索開始和對面對線 當達到特定狀況時觸發事件,而後執行對應的委託(即回調已經註冊了的李青的Help方法) 88 Yassuo.BackToBase(); //亞索血量太低迴城了 重置hp爲500 89 Yassuo.LowHpEvent -= Leesin.Help; //亞索回城了,李青再也不關注 90 Yassuo.fight(4); //亞索繼續和對面對線 當達到特定狀況時又觸發事件,但李青已經取消關注,事件處理爲空,因此直接打印亞索要說的話 91 Console.ReadLine(); //Pause 92 } 93 } 94 }
對應的控制檯輸出以下:
這裏歸納來講主要就是盲僧的Help方法註冊了亞索的低血量事件lowHpEvent,當亞索低血量時觸發了事件,而後經過相應的的委託回調了盲僧的Help方法。亞索回城後,盲仔取消了對亞索低血量事件的關注。因此以後亞索再次低血量時,由於沒有對應的事件處理程序(LowHpEvent == null),因此亞索開始吐槽沒人來上路。
參考的相關資料:
C#圖解教程(第四版) 做者:[美]Daniel M.Solis
大白話系列之C#委託與事件 :http://www.cnblogs.com/wudiwushen/archive/2010/04/20/1703368.html 做者:波哥2010
知乎回調函數相關: https://www.zhihu.com/question/1980113 。