关于委托和事件,多是.NET或者说是面向对象编程语言中的一个比较重要又比较难以理解的概念。关于这一话题,园子里的人也写了不少文章,最经典的可能就是张子阳的C#中的委托和事件这两篇文章了,以前也看过MSDN 上的WebCast深刻 "委托和事件"。可能和不少人同样,刚开始读的时候,以为很清楚,可是过了一段时间好像又忘记了委托和事件的区别,知道好久之前,在一次面试中我被问到委托和事件有什么区别,一会儿就说不清了。html
因此这里稍微理一下,也算是本身的一个总结。固然,仍是推荐你们先读前面推荐的两篇文章。面试
.NET中的事件模型是创建在委托(delegate)这一机制上的,因此首先来看看什么是委托。编程
委托是一种类型安全的调用回调方法,相似于C中的函数指针。委托(Delegate)是一个类,当建立实例时,须要传入方法名称,每个委托都有一个签名,好比:安全
delegate int SomeDelegate(string s, bool b);
这就是一个委托签名,他能够表明第一个参数类型为string类型,第二个参数类型为bool型,而且返回值为int的全部方法。编程语言
当建立代理实例时,须要传入一个方法名称做为构造函数。这个方法必须和代理定义的参数和返回值类型相同。好比:函数
private static int SomeFunction(string str, bool bln){...}
这样咱们就能够实例化一个SomeDelegate,并将SomeFunction做为参数传递进去了,由于它们有相同的签名:this
SomeDelegate sd = new SomeDelegate(SomeFunction);
如今sd指向了SomeFunction这个函数。若是调用sd,那么SomeFucntion函数就会被调用。spa
sd("SomeString", true);
定义了事件成员的类型容许类型或者类型的实例在某些特定事情发生时,通知其余对象。在日常的编程中,咱们不经意间已经接触到了事件,好比Button类,当点击时,就会触发Click事件,Timer类,每一秒就会触发tick事件。要了解事件,最简单的方法就是举个例子来讲明。举个通俗的猫叫,老鼠跑,主人被惊醒的例子。这也是面试时容易被问到的问题。解决方法就是使用事件,或者扩展开来就是观察者模式。线程
首先来实现Cat类,具体实现以下:代理
public class Cat { public delegate void MeowHandler(object sender, EventArgs s); public event MeowHandler Meow; protected virtual void OnMeow(EventArgs s) { MeowHandler temp = Meow; if (temp != null) { temp(this, s); } } public void StartMeow() { EventArgs args = new EventArgs(); Console.WriteLine("Cat start meow..."); OnMeow(args); } }
首先定义了一个public名为MeowHandler的代理,表示猫叫。方法签名很简单,一个名为Sender的Object参数和一个EventArgs的参数,返回值为空。接着定义一个名为Meow的事件。注意这里定义事件使用event参数,而后加一个代理的名称。定义成public供外部注册。
紧接着,咱们定义一个触发猫叫的方法OnMeow,用来通知全部已订阅Meow事件的对象。这里为了线程安全,在内部定义了一个temp对象,来保存Meow委托。若是不适用临时变量temp,方法只隐约Meow的话,在线程知道Meow不为null的时候,另一个线程讲NewMail改成null,这样就会引起异常。
将OnMeow定义为虚方法使得派生类能够直接控制事件是否发生。一般派生类只须要调用基类的OnMeow方法便可。可是在有些状况下,派生类能够选择不调用基类的OnMeow方法。
最后,定义了一个StartMeow方法,用来将外部的输入转换为引起事件的动做。当咱们调用StartMeow事,就会调用OnMeow方法,模拟事件发生。
接下来,须要定义Mouse类型:
public class Mouse { public Mouse(Cat cat) { cat.Meow += new Cat.MeowHandler(cat_Meow); } void cat_Meow(object sender, EventArgs s) { Console.WriteLine("Mouse run..."); } public void Unregister(Cat cat) { cat.Meow -= cat_Meow; } }
当初始化Mouse对象时,经过构造函数传入Cat对象,而后注册Cat的Meow事件。
最后,实例化一个Cat,而后将其做为参数传入Mouse的构造函数。当Cat的StartMeow方法调用时,Mouse的注册的Meow事件就会触发。
static void Main(string[] args) { Cat c = new Cat(); Mouse m = new Mouse(c); c.StartMeow(); Console.ReadKey(); }
能够看到,对于事件,咱们只能使用+=,和-=来进行注册和取消注册,没法来使用=进行赋值。在内部+=被转化为在委托链表上增长委托,-=被转化为在委托立案表上移除委托。限制了用户可以直接操做委托的权限。好比,若是没有事件,经过代理的话,只有把代理定义为public,这样,当某个用户已经注册了一系列的委托到委托列表上,正要执行的时候,另一个用户使用赋值语句=,给予了一个新的委托,或者直接将其设置为null,那么前一个用户的委托列表就会被丢失,这样严重破坏了对象的封装性。Event使得只能使用+=和-=操做符来进行事件的注册和取消注册。
对照字段和属性,委托和事件关系彷佛也是如此,它是对委托实例的一层封装,使得客户端不能随意更改或者重置内部的委托列表,只可以容许往列表中增长或者移除委托,使得代码具备更好的封装性。这样来看,事件其实只不过相似声明了一个进行了封装的委托类型变量而已。
本篇文章简单介绍了委托和事件的概念,算是本身作个笔记,更好的相关介绍推荐看以前两位前辈写的文章。下一篇文章将介绍事件内部的处理机制、事件与线程安全等。