预备知识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 。