第9讲事件1
现在来讲一下事件。我们知道Windows操作系统是一个基于事件的操作系统。掌握事件的机制对GUI编程能有着非常重要的意义。
事件是类在发生其关注的事情时来提供通知的一种方式。例如,封装用户界面控件的类可以定义一个在用户单击该控件时发生的事件。控件类不关心单击按钮时发生了什么,但它需要告知派生类单击事件已发生。然后,派生类可选择如何响应。
说到事件,就得谈谈2个角色
事件发行者(Publisher):
一个事件发行者,也称作发送者(sender),其实就是一个对象,这个对象会自行维护本身的状态信息。当本身状态信息变动时,便触发一个事件,并通知所有的事件订阅者。
事件订阅者(Subscriber)
对事件感兴趣的对象,也称为接收者(Receiver),可以注册感兴趣的事件,通常需提供一个事件处理程序,在事件发行者触发一个事件后,会自动执行这段代码的内容。
下面我们举一个简单的例子来说明事件,如图:
某出版社出版一种名为电脑的杂志,如果订户想订阅这种杂志呢,他必须到出版社去办理订阅的手续。这样,每当出版社出版了这种杂志以后,他就会把这个杂志发送给已经订阅的用户,供他们观看。在这个例子里面,事件是什么呢?事件就是这本杂志的出版,每隔一个月都会发生一次出版电脑杂志这个事件。当这个事件发生以后,出版社就会把这个杂志分发到已经订阅了这本杂志用户的手上。这样呢就形成了一个事件的机制。
好,下面我们来用代码来实现出版社和订户之间的故事。
我们看下效果,如图:
执行成功,每当出版社发行杂志,订阅者都会收到刊物。我们现所写的程序已经最简单的可以表示表现事件机制的这么一个程序,当然,在这个程序中我们还感到事件给我们带来了好处。但是我想,正是这么一个简单的程序,才能让大家初步的理解事件。为了能让大家更好的理解事件,也为了让出版社和订阅者之间的故事显的更加真实,我们来对程序进行一些改动:
我们来看执行的效果,如图:
我们来看一下执行的过程,首先建立了出版社,接下来呢创建了张三这么一个用户,他订阅了电脑杂志。然后创建了李四用户,李四不但订阅了电脑杂志而且订阅了生活杂志。当发行了这2本杂志之后,我们可以在屏幕上看到张三和李四都收到了各自订阅的杂志(李四收到2本,张三只有一本电脑杂志,因为张三并没有订阅生活杂志)。好,一年以后李四不再订阅电脑杂志,这时候继续发行这2本杂志,从屏幕上可以看出李四只收到了生活杂志,并没有收到电脑杂志,因为她推出了订阅电脑杂志。
从这里我们可以看出,事件发行者只会把事件发送给那些订了该事件的用户,如果有多个用户订阅了同一个事件,那么该事件触发时,它就会把消息发送给多个用户。
通过这个例子我们可以感受到一点点事件给我们带来的好处,也让我们进一步了解了事件的机制。
事件是类在发生其关注的事情时来提供通知的一种方式。例如,封装用户界面控件的类可以定义一个在用户单击该控件时发生的事件。控件类不关心单击按钮时发生了什么,但它需要告知派生类单击事件已发生。然后,派生类可选择如何响应。
说到事件,就得谈谈2个角色
事件发行者(Publisher):
一个事件发行者,也称作发送者(sender),其实就是一个对象,这个对象会自行维护本身的状态信息。当本身状态信息变动时,便触发一个事件,并通知所有的事件订阅者。
事件订阅者(Subscriber)
对事件感兴趣的对象,也称为接收者(Receiver),可以注册感兴趣的事件,通常需提供一个事件处理程序,在事件发行者触发一个事件后,会自动执行这段代码的内容。
下面我们举一个简单的例子来说明事件,如图:
某出版社出版一种名为电脑的杂志,如果订户想订阅这种杂志呢,他必须到出版社去办理订阅的手续。这样,每当出版社出版了这种杂志以后,他就会把这个杂志发送给已经订阅的用户,供他们观看。在这个例子里面,事件是什么呢?事件就是这本杂志的出版,每隔一个月都会发生一次出版电脑杂志这个事件。当这个事件发生以后,出版社就会把这个杂志分发到已经订阅了这本杂志用户的手上。这样呢就形成了一个事件的机制。
好,下面我们来用代码来实现出版社和订户之间的故事。
- using System;
- //定义一个类Publisher,就是出版社
- class Publisher //出版社
- {
- //出版社有发行刊物这么一个事件,只有出版社发行了刊物,订户才能收到刊物并阅读
- //事件是通过委托的机制而达成的,因此诺要定义一个事件首先事件的发行者要先定义一个委托类型
- //这里我们我们定义了一个委托类型Publish就是发行的意思,它没有返回值也没有参数
- public delegate void Publish(); //声明事件所需的代理
- //事件的声明使用event这个保留字,然后将事件的名称和委托类型结合在一起
- public event Publish OnPublish; //事件的声明
- //这里我们声明了一个OnPublish事件,它跟Publish这个委托类型关联在了一起
- //当OnPublish触发事便可以通过Publish这个委托类型回调事件处理程序
- //事件的触发是通过一个方法来实现的,下面我们来定义这个触发事件的方法(发行)
- public void issue() //触发事件的方法
- {
- //当OnPublish不为null时才触发事件
- if (OnPublish != null)
- {
- //在触发事件前,我们在屏幕上打印“发行刊物”,表示一个事件已被触发
- Console.WriteLine("发行刊物");
- //事件的触发非常简单,使用事件的名称后面加括号就行了
- OnPublish();
- }
- }
- }
- //出版社已经建成了,但是出版社要生存就必须要有用户去订阅它的杂志,否则出版社的存在就失去了意义
- //下面我们来建立一个订阅者的类
- //声明一个订阅者的类
- class Subcriber //订阅者
- {
- //接下来就要在事件订阅者中定义事件处理程序
- //这里我们声明了一个Receive方法,作为一个事件处理的程序
- //也就是说当一个事件发生订阅者收到消息以后它所做的事情
- public void Receive() //在事件订阅者中定义事件处理程序
- {
- //这个事情非常的简单,就是打印出“订阅者已经收到了刊物”表示该订阅者已经收到了消息
- Console.WriteLine("订阅者已经收到了刊物");
- }
- //以上的Receive方法要注意事件处理程序中所定义的方法必须跟委托类型一一对应
- //也就是Publish委托是一个没有返回值没有参数的委托类型,而你所定义的方法Receive也必须是没有返回值没有参数的
- //这一点在上一次课的委托中我们已经讲到
- }
- //好,出版社有了订阅者也有了,我们就可以开始演艺他们之间的故事了
- class Story
- {
- static void Main()
- {
- //首先建立以一个出版社
- Publisher Pub = new Publisher();
- //然后建立一个订阅者张三
- Subcriber zs = new Subcriber();
- //接下来张三去出版社订阅刊物
- Pub.OnPublish += new Publisher.Publish(zs.Receive); //向事件发行者订阅一个事件
- //每当OnPublish事件发生以后,它都会触发zs.Receive方法
- //这里使用了+=操作符来订阅事件
- //事件的本质其实就是一个委托链
- //最后出版社在某一天出版了杂志
- Pub.issue(); //触发事件
- }
- }
执行成功,每当出版社发行杂志,订阅者都会收到刊物。我们现所写的程序已经最简单的可以表示表现事件机制的这么一个程序,当然,在这个程序中我们还感到事件给我们带来了好处。但是我想,正是这么一个简单的程序,才能让大家初步的理解事件。为了能让大家更好的理解事件,也为了让出版社和订阅者之间的故事显的更加真实,我们来对程序进行一些改动:
- using System;
- class Publisher //出版社
- {
- //经过一段时间的发展,出版社取得了一定的业绩,决定扩大规模,出版2种杂志
- //第一种杂志叫电脑,并带一个参数magazineName就是杂志的名称
- public delegate void PubComputer(string magazineName);
- //第2种杂志叫生活
- public delegate void PubLife(string magazineName);
- //声明对应的事件
- public event PubComputer OnPubComputer;
- public event PubLife OnPubLife;
- //首先声明发行电脑杂志所需要的方法
- public void issueComputer()
- {
- if (OnPubComputer != null)
- {
- Console.WriteLine("发行《电脑》杂志");
- //根据所定义的委托PubComputer,触发事件的后面也必须带个参数
- OnPubComputer("电脑杂志");
- }
- }
- //声明发行生活杂志所需要的方法
- public void issueLife()
- {
- if (OnPubLife != null)
- {
- Console.WriteLine("发行《生活》杂志");
- OnPubLife("生活杂志");
- }
- }
- }
- class Subcriber //订阅者
- {
- //在订阅者类中声明一个私有字段name,来表述订阅者的姓名
- private string name;
- //声明一个构造函数,给name赋值
- public Subcriber(string name)
- {
- this.name = name;
- }
- //由于在委托中,委托类型中带了个字符串参数,所以Receive也必须要带一个字符串参数
- public void Receive(string magazineName)
- {
- Console.WriteLine(name + "已经收到" + magazineName);
- }
- }
- //演艺他们之间的故事
- class Story
- {
- static void Main()
- {
- //建立以一个出版社
- Publisher Pub = new Publisher();
- //创建一个订阅者张三
- Subcriber zs = new Subcriber("张三");
- //接下来张三去订阅了电脑杂志
- Pub.OnPubComputer += new Publisher.PubComputer(zs.Receive);
- //再创建一个订阅者李四
- Subcriber ls = new Subcriber("李四");
- //李四订阅了电脑杂志,还订阅了生活杂志
- Pub.OnPubComputer += new Publisher.PubComputer(ls.Receive);
- Pub.OnPubLife += new Publisher.PubLife(ls.Receive);
- //我们看这里上面的语句,调用事件名称的时候,使用的是对象名称也就是实例名称Pub
- //而调用委托类型的时候我们使用的是类名称Publisher
- //为什么会不一样呢?委托类型本身就是一个静态的类型,静态的类所以只能用类名称来调用
- //当然你也可以把事件写成静态的,这样也就可以用类直接调用了
- //接下来出版社发行电脑杂志
- Pub.issueComputer(); //触发事件
- //紧接着出版社又发行了生活杂志
- Pub.issueLife();
- //打印个空行
- Console.WriteLine();
- Console.WriteLine("一年以后");
- //李四不再订阅电脑杂志了
- Pub.OnPubComputer -= new Publisher.PubComputer(ls.Receive);
- //接着出版社继续发行这2种杂志
- Pub.issueComputer();
- Pub.issueLife();
- }
- }
我们来看一下执行的过程,首先建立了出版社,接下来呢创建了张三这么一个用户,他订阅了电脑杂志。然后创建了李四用户,李四不但订阅了电脑杂志而且订阅了生活杂志。当发行了这2本杂志之后,我们可以在屏幕上看到张三和李四都收到了各自订阅的杂志(李四收到2本,张三只有一本电脑杂志,因为张三并没有订阅生活杂志)。好,一年以后李四不再订阅电脑杂志,这时候继续发行这2本杂志,从屏幕上可以看出李四只收到了生活杂志,并没有收到电脑杂志,因为她推出了订阅电脑杂志。
从这里我们可以看出,事件发行者只会把事件发送给那些订了该事件的用户,如果有多个用户订阅了同一个事件,那么该事件触发时,它就会把消息发送给多个用户。
通过这个例子我们可以感受到一点点事件给我们带来的好处,也让我们进一步了解了事件的机制。