標籤:[] 發布 自動 12px https pre www 教程 elb
預備知識
在學習委託和事件之前,我們需要知道的是,很多程式都有一個共同的需求,即當一個特定的程式事件發生時,程式的其他部分可以得到該事件已經發生的通知。
而發行者/訂閱者模式可以滿足這種需求。簡單來說,在這種模式中,發行者定義了一系列程式的其他部分可能感興趣的事件。其他類可以“註冊”,以便再這些事件發生時發行者可以通知它們。這些訂閱者類通過向發行者提供一個方法來“註冊”以擷取通知。當事件發生時,發行者“觸發事件”,然後執行訂閱者提交的所有事件。
註:由訂閱者提供的方法稱為回調方法,或者說回呼函數,關於回呼函數可以參考https://www.zhihu.com/question/1980113 。
大概瞭解了下基本機制後,我們再來看看委託和事件:
什麼是委託?
委託和類一樣,是一種使用者自訂的類型。但類表示的是資料和方法的集合,而委託則持有一個或多個方法,以及一系列預定義操作(你可以暫時把它理解成一個型別安全的C++函數指標)。可以通過以下操作來使用委託:
1)聲明一個委託類型(類似方法聲明,但委託沒有實現塊)
public delegate void MyDel(int x);//格式為[修飾符] [委託關鍵字delegate] [傳回型別][委託名][簽名]
2)使用該委託型別宣告一個委託變數。
MyDel delVar;
3)建立委託類型的對象,把它賦值給委派物件。新的委派物件包括指向某個方法的引用,這個方法和第一步的簽名和傳回型別一致。
建立委派物件可以這樣:
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個人按職責劃分為上單、打野、輔助、中單、輔助。這裡主要是通過打野和上單的一個簡單互動來說明一下事件的觸發機制。具體的代碼如下:
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 else55 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 Program79 {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為50089 Yassuo.LowHpEvent -= Leesin.Help; //亞索回城了,李青不再關注90 Yassuo.fight(4); //亞索繼續和對面對線 當達到特定情況時又觸發事件,但李青已經取消追蹤,事件處理為空白,所以直接列印亞索要說的話91 Console.ReadLine(); //Pause92 }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 。
C#學習(一):委託和事件