目录
发布者和订阅者
发布者/订阅者模式中,发布者类 定义了一系列程序的其他部分可能感兴趣的事件。其他类可以“注册”,以便在这些事件发生时发布者可以通知它们。这些 订阅者类 通过向发布者提供一个方法来“注册”以获取通知。当事件发生时,发布者 “触发事件” ,然后执行订阅者提交的所有事件
由订阅者提供的方法称为 回调方法 ,因为发布者通过执行这些方法来 “往回调用订阅者的方法”,还可以将他们称为 事件处理程序,因为它们是为处理事件而调用的代码。
一些有关事件的重要事项:
事件的私有委托:
事件提供了对它的私有控制委托的结构化访问。也就是说,你无法直接访问委托
事件中可用的操作比委托少,对于事件我们只可以添加,删除或调用事件处理程序。
事件被触发时,他调用委托来依次调用调用列表中的方法
+= 和 -= 运算符是事件唯一允许的操作(除了调用事件本身)
源代码组件概览
需要在事件中使用的代码有5部分:
委托类型声明 事件和事件处理程序必须有共同的签名和返回类型,它们通过委托类型进行描述
事件处理程序声明 订阅者类中会在事件触发时执行的方法声明。它们不一定是有显式命名的方法,还可以是匿名方法或者Lambda 表达式
事件声明 发布者类必须声明一个订阅者可以注册的事件成员。当声明的事件为public时,称为发布了事件
事件注册 订阅者必须订阅事件才能在它被触发时得到通知
触发事件的代码 发布者类中“ 触发 ”事件并导致调用注册的所有事件处理程序的代码
声明事件
事件声明在一个类中
它需要委托类型的名称,任何附加到事件(如注册)的处理程序都必须与委托类型的签名和返回类型匹配
它声明为public,这样其他类和结构可以在它上面注册事件处理程序
不能使用对象创建表达式(new表达式)来创建它的对象
可以通过逗号分隔的列表在一个声明语句中声明一个以上的事件
可以使用 static 关键字让事件变成静态
事件是成员
由于事件是成员:
我们不能在一段可执行代码中声明事件
它必须声明在类或结构中,和其他成员一样
事件成员被隐式自动初始化为null
订阅事件
对于一个要添加到事件的事件处理程序来说,它必须具有与事件的委托相同的返回类型和签名
使用 += 运算符来为事件增加事件处理程序
触发事件
事件成员本身只是保存了需要被调用的事件处理程序。如果时间没有被触发就什么都不会发生
- using System;
- using System.Collections.Generic;
- using System.Text;
-
- namespace Csharpzuoye
- {
- delegate void Handler();
-
- //发布者:
- class Incrementer
- {
- public event Handler CountedADozen; //创建事件并发布
-
- public void DoCount()
- {
- for(int i = 1;i<100;i++)
- {
- if (i % 12 == 0 && CountedADozen != null)
- CountedADozen(); //每增加12个计数触发事件一次
- }
- }
- }
-
- //订阅者:
- class Dozens
- {
- public int DozensCount { get; private set; }
- //构造函数
- public Dozens(Incrementer incrementer)
- {
- DozensCount = 0;
- incrementer.CountedADozen += IncrementDozensCount; //订阅事件
- }
- //声明事件处理程序
- void IncrementDozensCount()
- {
- DozensCount++;
- }
- }
-
- class Program
- {
- static void Main()
- {
- Incrementer incrementer = new Incrementer();
- //构造函数直接订阅事件,一步到位
- Dozens dozensCounter = new Dozens(incrementer);
-
- incrementer.DoCount();
- Console.WriteLine("Number of dozens = {0}", dozensCounter.DozensCount);
- }
- }
- }
标准事件的用法
GUI编程是事件驱动的,也就是说在程序运行时,它可以在任何时候被事件打断,比如按钮点击,按下按键或系统定时器
事件使用的标准模式的根本就是System命名空间声明的EventHandler委托类型。
EventHandler 委托类型的第二个参数是EventArgs类对象,它的声明在System命名空间中
不管参数使用什么的实际类型是什么,object类和EventArgs总是基类,这样EventHandler就能提供一个对所有事件和事件处理器都通用的签名,只允许两个参数,而不是各自都有不同的签名
标准使用对比:
通过扩展EventArgs来传递数据
声明一个派生自EventArgs的自定义类,可以保存我们传入的数据,向事件处理程序的第二个参数传入数据
类的名称应该以EventArgs结尾
泛型委托
移除事件处理程序 -=
事件访问器