一、发布者/订阅者模式
1. 订阅者发布者的功能:
- 发布者(类或结构)定义了一系列程序的其他部分可能感兴趣的事件。
- 订阅者(类或结构)通过向发布者提供一个方法(称为回调方法,也可以称为事件处理程序)来“注册”以获取通知。
- 当事件发生时,发布者“触发事件”,然后执行订阅者提交的所有事件。
2.事件与委托的联系:
事件就像用于某种特殊用途的简单委托。事件包含了一个私有的委托。
有关事件中的私有委托的重点如下:
- 无法直接访问事件中的委托。
- 事件中的可用操作比委托要少,我们只能添加(+=)、删除(-=)或调用事件处理程序。
- 事件被触发时,它调用委托来依次调用调用列表中的方法。
事件中使用的五部分代码:
- 委托类型声明:事件和事件处理程序必须有相同的签名和返回类型,通过委托描述。
- 事件处理程序声明:订阅者类中会在事件触发时执行的方法声明。
- 事件声明:发布者类必须声明一个订阅者类可以注册的事件成员。当声明的事件为public时称发布了事件。
- 事件注册:订阅者必须订阅事件才能在它被触发时得到通知。
- 触发事件的代码:发布者类中触发事件并导致调用注册的所有事件处理程序的代码。
二、声明事件
事件是成员。
- 因为事件是成员,所以必须声明在类或结构里,方法的外面。
- 事件成员被隐式自动初始化为null。
- 声明事件时需要使用event关键字。
- 不能使用对象创建表达式(new表达式),来创建它的对象。
- 它声明为public,这样其他类或结构可以在上面“注册”事件处理程序。【发布事件】
- 可以使用static让事件变为静态的。
(BCL专门声明了一个叫做EventHandler的委托专门用于系统事件)
三、订阅事件
订阅者向事件添加事件处理程序。(必须具有和事件的委托相同的签名和返回类型)
- 使用“+=”运算符为事件增加事件处理程序。
- 事件处理程序的规范可以是实例方法名、静态方法名、匿名方法或Lambda表达式。
四、触发事件
事件成员本身只是保存了订阅的事件处理程序,需要代码触发这些事件处理程序。注意事项如下:
- 触发事件之前需要判断事件是否为null。
- 触发事件的语法和调用方法一样。
包含了发布者和订阅者的完整程序,展示了使用事件所必须的五部分,代码示例如下:
//声明委托
delegate void Handler();
//发布者
class Incrementer
{
public event Handler CountedADozen;//依赖委托声明事件,(public)发布事件
//触发事件的代码
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++;
}
}
//测试
internal class Program
{
static void Main(string[] args)
{
Incrementer incrementer = new Incrementer();
Dozens dozensCount = new Dozens(incrementer);
incrementer.DoCount();
Console.WriteLine("事件处理程序一共被调用了:{0}次", dozensCount.DozensCount);
}
}
五、标准事件的用法
1. 系统提供的事件的标准委托类型EventHandler
对于事件的使用,.NET框架提供了一个标准模式。事件使用的根本模式就是使用System命名空间声明的EventHandler委托类型。
下面是将委托类型换为处理事件的标准委托类型EventHandler代码示例:
//发布者
class Incrementer
{
public event EventHandler CountedADozen;//使用系统提供的标准事件委托
//触发事件的代码
public void DoCount()
{
for (int i = 1;i < 100; i++)
if( i % 12 == 0 && CountedADozen != null)
CountedADozen(this,null);//每隔12个数触发一次事件
}
}
//订阅者
class Dozens
{
public int DozensCount { get; private set; }
public Dozens(Incrementer incrementer)
{
DozensCount = 0;
incrementer.CountedADozen += IncrementDozensCount;//订阅事件:将事件处理程序添加到事件的委托中
}
//声明事件处理程序
void IncrementDozensCount(object source,EventArgs e)
{
DozensCount++;
}
}
//测试
internal class Program
{
static void Main(string[] args)
{
Incrementer incrementer = new Incrementer();
Dozens dozensCount = new Dozens(incrementer);
incrementer.DoCount();
Console.WriteLine("事件处理程序一共被调用了:{0}次", dozensCount.DozensCount);
}
}
2. 扩展EventArgs来传递数据
为了使用系统提供的标准事件的委托类型EventHandler中的第二个参数EventArgs来传递数据,需要一个派生自EventArgs的类。
代码示例如下:
//扩展EventArgs的类用来传递数据
public class IncrementEventArgs : EventArgs
{
public int IterationCount { get; set; }//存储整数
}
//发布者
class Incrementer
{
public event EventHandler<IncrementEventArgs> CountedADozen;//泛型委托使用自定义类
//触发事件的代码
public void DoCount()
{
//自定义类对象
IncrementEventArgs args = new IncrementEventArgs();
for (int i = 1; i < 100; i++)
if (i % 12 == 0 && CountedADozen != null)
{
args.IterationCount = i;
CountedADozen(this, args);//每隔12个数触发一次事件,在触发事件时传递参数
}
}
}
//订阅者
class Dozens
{
public int DozensCount { get; private set; }
public Dozens(Incrementer incrementer)
{
DozensCount = 0;
incrementer.CountedADozen += IncrementDozensCount;//订阅事件:将事件处理程序添加到事件的委托中
}
//声明事件处理程序
void IncrementDozensCount(object source,IncrementEventArgs e)
{
Console.WriteLine("事件在第{0}次被触发,触发事件的对象:{1}",e.IterationCount,source.ToString());
DozensCount++;
}
}
//测试
internal class Program
{
static void Main(string[] args)
{
Incrementer incrementer = new Incrementer();
Dozens dozensCount = new Dozens(incrementer);
incrementer.DoCount();
Console.WriteLine("事件处理程序一共被调用了:{0}次", dozensCount.DozensCount);
}
}
代码运行结果如下:
3.移除事件
可以将事件处理程序从事件中移除。
- 使用“-=”运算符将事件处理程序移除。
- 如果一个处理程序像事件注册了多次,那么当移除该处理程序时,将只移除列表中该处理程序的最后一个示例。
代码示例:
//发布者
class Publisher
{
public event EventHandler SimpleEvent;//声明事件
public void RaiseTheEvent()
{
SimpleEvent(this, null);//触发事件
}
}
//订阅者
class Subscriber
{
public void MethodA(object o, EventArgs e) { Console.WriteLine("AAA"); }//事件处理程序
public void MethodB(object o, EventArgs e) { Console.WriteLine("BBB"); }//事件处理程序
}
//测试
internal class MyTestClass
{
static void Main(string[] args)
{
Publisher publisher = new Publisher();//发布者对象
Subscriber subscriber = new Subscriber();//订阅者对象
//订阅事件
publisher.SimpleEvent += subscriber.MethodA;
publisher.SimpleEvent += subscriber.MethodB;
//触发事件
publisher.RaiseTheEvent();
Console.WriteLine("----移除事件处理程序MethodB----");
//移除事件
publisher.SimpleEvent -= subscriber.MethodB;
//触发事件
publisher.RaiseTheEvent();
}
}
(注:本章学习总结自《C#图解教程》)