事件 你怎么看?
一 前言
最近在阅读 ASP.NET 本质论 一书详了解了“处理管道”这一概念,处理管道是通过事件机制来实现的,事件?这个知识点对我来说只是听过这个词,但是很少自己全面了解过,到于用嘛,当初在asp.net 拖控件的时候用得是相当多啊。于是便全面了解了一下,其实事件应该与委托一起写的,但是当时并不了解事件所以就不知道它俩之间的关系。
不知不觉 XX 你怎么看?系列也写到第三篇了
1.1 事件结构
发布者:让事件被其它类或结构可见并使用的类或结构,有点绕 简单来说就是 声明事件的类或者结构
订阅者:把事件和发布者关联注册的类或结构,简单说就是负责将方法注册到事件上的类或结构
事件处理程序 :注册到事件上的方法,就是订阅者往事件上注册的方法,当事件被触发时所调用的方法就是从这里来的
用MSDN上的话说就是 发送(或引发)事件的类称为“发行者”,接收(或处理)事件的类称为“订阅者”,而执行事件中的方法所在的类或结构就是 事件处理程序。
1.2 事件核心对象
EventHandler:本质上是一个委托,而且是.NET BCL 使用的预定义的用于标准事件的委托,public delegate void EventHandler(object sender, EventArgs e)
要注意这个委托是有两个参数的,所以在声明事件,与事件处理程序时要保持与它一样的函数签名。
EventArgs:表示包含事件数据的类的基类,并提供要用于不包含事件数据的事件的值,而且不能传递数据,如果要传递数据必须声明一个从EventArgs继承的基类
上面都是概念性的知识,一时半会不了解也没关系,我会在下文的例子中再强化一次的,这样会了解更深刻一点
event:声明事件的关键字
二 标准事件
标准事件指得是.net Framework 定义的标准事件,
事件的调用流程大至如下图,下图是我根据自己的理解画的如有不对还请指出
2.1 事件发布者
事件发布者 定义了一个事件,并提供了触发事件的代码,发布者定义了事件之后,订阅者就可以去订阅事件了
事件的声明方法也有如下3种 单个声明,多个声明,以及声明静态事件,另外事件是成员不是类型,所以不能用new 来创建它,也只能声明在类或结构中,事件成员被隐式自动初始化为null 也就有代码中if (MyEvent != null)的判断,判断事件中是否有成员。
//事件发布者 class PublishEvent { //声明一个事件MyEvent public event EventHandler MyEvent; //也可以一次声明多个事件 public event EventHandler MyEvent1 , MyEvent2 , MyEvent3; //也可以是静态的事件 public static event EventHandler StaticEvent; public System.Timers.Timer myTimer; //在调用构造函数时触发事件 public PublishEvent() { myTimer = new System.Timers.Timer(); //Elapsed也是一个事件,我们把OnChange方法也注册到这个事件上,所以当Elapsed事件被触发时OnChange方法也会被触发 myTimer.Elapsed += OnOneSecond; //设置触发Elapsed事件的间隔时间 myTimer.Interval = 1000; //设置是否触发Elapsed事件 myTimer.Enabled = true; } public void OnOneSecond(object source, EventArgs e) { if (MyEvent != null) { //触发事件 MyEvent(source, e); } } }
2.2 事件订阅者
事件订阅者主要把方法注册到事件上,因为我们声明事件时是这么声明的public event EventHandler MyEvent;
这个事件的是EventHandler委托类型的,而EventHandler本质是一个委托,所以会发现给事件注册方法与给委托绑定方法是一样的,而且同样支持匿名方法,Lambda表达示
//事件订阅者 class Program { static void Main(string[] args) { ProsessEvent prosessEvent = new ProsessEvent(); PublishEvent publish = new PublishEvent(); /*注册事件处理程序*/ //实例方法 publish.MyEvent += prosessEvent.TimerHandler; //匿名方法 publish.MyEvent += delegate(object source, EventArgs e) { Console.Write("这是匿名处理程序\n"); }; //Lambda表达示 publish.MyEvent += (object source, EventArgs e) => Console.Write("这是Lambda处理程序\n"); Console.Read(); } }
2.3 事件处理程序
这个事件处理程序说白了那就是给事件注册方法具体实现的地方可以是类中或者结构或者 匿名方法和Lambda表达示
//事件处理程序 class ProsessEvent { public void TimerHandler(object sorce, EventArgs e) { Console.Write("处理程序方法被调用了\n"); } }
把这三个对象分开讲解后,相信对 发布者,订阅者,事件处理程序 应该了解得更透彻一点,下面是把三个对象整合的代码
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Timers; namespace EventDemo { //事件订阅者 class Program { static void Main(string[] args) { ProsessEvent prosessEvent = new ProsessEvent(); PublishEvent publish = new PublishEvent(); /*注册事件处理程序*/ //实例方法 publish.MyEvent += prosessEvent.TimerHandler; //匿名方法 publish.MyEvent += delegate(object source, EventArgs e) { Console.Write("这是匿名处理程序\n"); }; //Lambda表达示 publish.MyEvent += (object source, EventArgs e) => Console.Write("这是Lambda处理程序\n"); Console.Read(); } } //事件发布者 internal class PublishEvent { //声明一个事件MyEvent public event EventHandler MyEvent; //也可以一次声明多个事件 public event EventHandler MyEvent1 , MyEvent2 , MyEvent3; //也可以是静态的事件 public static event EventHandler StaticEvent; public System.Timers.Timer myTimer; //在调用构造函数时触发事件 public PublishEvent() { myTimer = new System.Timers.Timer(); //Elapsed也是一个事件,我们把OnChange方法也注册到这个事件上,所以当Elapsed事件被触发时OnChange方法也会被触发 myTimer.Elapsed += OnOneSecond; //设置触发Elapsed事件的间隔时间 myTimer.Interval = 1000; //设置是否触发Elapsed事件 myTimer.Enabled = true; } public void OnOneSecond(object source, EventArgs e) { if (MyEvent != null) { //触发事件 MyEvent(source, e); } } } //事件处理程序 class ProsessEvent { public void TimerHandler(object sorce, EventArgs e) { Console.Write("处理程序方法被调用了\n"); } } }
结果如下 因为定时器会每隔一秒触发一次Elapsed事件所以程序会不断执行
三 自定义事件
3.1 自定义事件类型,事件发布者
标准的事件是EventHandler委托类型,它的第二个参数是EventArgs类型,而EventArgs是不能传递任何数据的,现在我们需要传递数据,
所以要定义一个类并继承EventArgs并定义一个字段Name用于保存数据。
那现在肯定不能再用EventHandler这个委托来做为事件的委托类型了,然后自己声明一个与EventHandler结构一致的委托MySelfEventHandler
并把第二个参数改成我们自己定义的类MySelfEventArgs,然后重新声明一个事件 MyEvent ,如此一来就完成了一个自定义的事件
//自定义事件类型 class MySelfEventArgs:EventArgs { public string Name; public MySelfEventArgs(string name) { Name = name; } } //事件发布者 internal class PublishEvent { /* 因为原来的事件类型EventHandler是.net 已经定义好的事件类型的委托, * 如果我们要自定义一个事件类型,则先需要定义一个事件类型的委托 */ public delegate void MySelfEventHandler(object source, MySelfEventArgs e);//原本为EventArgs //声明一个自定义事件MyEvent public event MySelfEventHandler MyEvent; public System.Timers.Timer myTimer; //在调用构造函数时触发事件 public PublishEvent() { myTimer = new System.Timers.Timer(); //Elapsed也是一个事件,我们把OnChange方法也注册到这个事件上,所以当Elapsed事件被触发时OnChange方法也会被触发 myTimer.Elapsed += OnOneSecond; //设置触发Elapsed事件的间隔时间 myTimer.Interval = 1000; //设置是否触发Elapsed事件 myTimer.Enabled = true; } public void OnOneSecond(object source, EventArgs e) { if (MyEvent != null) { //因为在触发事件时要传入自定义类型的事件 MySelfEventArgs selfEventArgs= new MySelfEventArgs("这是自定义事件"); //触发事件 MyEvent(source, selfEventArgs); } } }
3.2 事件订阅者
这里还是一样把方法注册到事件中
//事件订阅者 class Program { static void Main(string[] args) { ProsessEvent prosessEvent = new ProsessEvent(); PublishEvent publish = new PublishEvent(); //注册方法 publish.MyEvent += prosessEvent.TimerHandler; Console.Read(); } }
3.3 事件处理程序
这里唯一的变化就是把原来的EventArgs改成自定义的MySelfEventArgs
//事件处理程序 class ProsessEvent { public void TimerHandler(object sorce, MySelfEventArgs e) { Console.Write("{0}\n",e.Name); } }
整合后的代码
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Timers; namespace EventDemo { //事件订阅者 class Program { static void Main(string[] args) { ProsessEvent prosessEvent = new ProsessEvent(); PublishEvent publish = new PublishEvent(); //注册方法 publish.MyEvent += prosessEvent.TimerHandler; Console.Read(); } } //自定义事件类型 class MySelfEventArgs:EventArgs { public string Name; public MySelfEventArgs(string name) { Name = name; } } //事件发布者 internal class PublishEvent { /* 因为原来的事件类型EventHandler是.net 已经定义好的事件类型的委托, * 如果我们要自定义一个事件类型,则先需要定义一个事件类型的委托 */ public delegate void MySelfEventHandler(object source, MySelfEventArgs e);//原本为EventArgs //声明一个自定义事件MyEvent public event MySelfEventHandler MyEvent; public System.Timers.Timer myTimer; //在调用构造函数时触发事件 public PublishEvent() { myTimer = new System.Timers.Timer(); //Elapsed也是一个事件,我们把OnChange方法也注册到这个事件上,所以当Elapsed事件被触发时OnChange方法也会被触发 myTimer.Elapsed += OnOneSecond; //设置触发Elapsed事件的间隔时间 myTimer.Interval = 1000; //设置是否触发Elapsed事件 myTimer.Enabled = true; } public void OnOneSecond(object source, EventArgs e) { if (MyEvent != null) { //因为在触发事件时要传入自定义类型的事件 MySelfEventArgs selfEventArgs= new MySelfEventArgs("这是自定义事件"); //触发事件 MyEvent(source, selfEventArgs); } } } //事件处理程序 class ProsessEvent { public void TimerHandler(object sorce, MySelfEventArgs e) { Console.Write("{0}\n",e.Name); } } }
运行结果如下
四 总结
个人感觉事件相对C#的其它知识点来说,算是比较难一点的,当然我只是以我个人花的时间来判断的,每个人情况也许不一样,之所以花的时间多,我觉得是在于事件所涉及的三个对象 "发布者","订阅者","事件处理程序" 之间的关系,至少我在理解这个关系时花了点时间。
把这篇文章写完,我都不觉得我对事件了解的有多深,我觉得还只是了解了事件最基本的用法。因为每一个阶段看到的东西是不一样的,我现在的阶段我想还只能看到这些。
如果您觉得本文有给您带来一点收获,不妨点个推荐,为我的付出支持一下,谢谢~
如果希望在技术的道路上能有更多的朋友,那就关注下我吧,让我们一起在技术的路上奔跑