Event事件和发布者/订阅者模式
很多程序都有这样的一个需求,当一个特定的事件发生时,程序的其他部分能够得到通知,并且需要做一些事情。这个时候就需要事件了。
发布者/订阅者模式
发布者/订阅者模式(publisher/subscriber pattern)就是满足这种需求,设计模式中也叫观察者模式。发布者存储一个方法集合,并且提供一个注册方法,让订阅者把自己的方法注册进去,这样在事件发生的时候,发布者可以调用注册到存储集合中的所有方法。
有以下要点:
- 发布者(publisher)
- 订阅者(subscriber)
- 事件处理程序(event handler)订阅者注册到发布者的方法
- 触发(raise)事件 当事件被调用(invoke)或触发(fire)时,所有注册的事件处理程序会被依次调用
使用步骤
- 声明委托类型
- 声明事件
- 事件处理程序声明
- 订阅事件/注册事件
- 触发事件(调用事件)
代码示例:
using EventDemo;
void Main()
{
Publisher pub = new Publisher(); //发布者对象
Subscriber sub = new Subscriber(); //订阅者对象
pub.BtnPressEvent += sub.ProcBtnPress; //4、订阅事件(或者说给事件添加处理程序)
pub.ButtonPress(); //5、触发事件
}
namespace EventDemo
{
//1、声明委托类型
delegate void DelButton(object sender, int id);
//发布者类型
class Publisher
{
//2、声明一个事件成员变量
public event DelButton BtnPressEvent;
//事件触发方法
public void ButtonPress()
{
Button button1 = new Button();
if (BtnPressEvent != null)
{
BtnPressEvent(button1, 10);
}
}
}
//订阅者类型
class Subscriber
{
public void ProcBtnPress(object sender, int id)
{
Console.WriteLine($"Id of button is {id}");
}
}
}//namespace
1、声明委托类型
//1、声明委托类型
delegate void DelButton(object sender, int id);
事件的声明需要依赖此委托类型,并且事件处理程序的格式要与此委托保持一致。事件与委托很像,它包含了一个私有的内部委托。
- 事件的委托是私有的,无法被直接访问
- 事件提供的操作比委托少,一般只有添加、删除和调用事件处理程序
- 事件触发时,它通过调用内部的委托,来依次调用存放在委托方法列表中的事件处理程序
2、声明事件变量
语法格式:
public event 委托类型 事件名称;
//2、声明一个事件成员变量,并且被隐式自动初始化为null
public event DelButton BtnPressEvent;
public static event DelButton BtnPressEvent; //声明为静态成员,局部于类
public event DelButton Event1, Event2, Event3; //同时声明多个事件
注意,事件是一个成员变量,而不是类型,它可以声明在类和结构中。这里使用public修饰后,外部的程序才可以向该事件注册处理程序。
BCL中有一个EventHandler的委托,专门用于系统事件。
3、事件处理程序
在订阅者类型中,定义了事件处理程序,该方法的格式必须保持和委托类型一致,要有相同的返回值和方法签名。该事件处理程序用于在事件触发时,被事件调用。最典型的就是响应winform或wpf中控件的事件,譬如像按钮按下、下拉框选择更改、文本框内容修改的事件等等。
//订阅者类型
class Subscriber
{
//3、事件处理程序(按钮按下处理)
public void ProcBtnPress(object sender, int id)
{
Console.WriteLine($"Id of button is {id}");
}
}
4、订阅事件
有了事件处理程序后,就可以把我们添加的方法注册到事件上了,或者说订阅该事件。在示例中,我们把自定义的事件处理方法ProcBtnPress通过+=的方式,添加到事件中去,这和委托增加方法的方式一致。
这样一来,可以把多个订阅者对象的自定义方法添加到事件中,又叫做多个订阅者订阅了同一个发布者的事件。
void Main()
{
Publisher pub = new Publisher(); //发布者对象
Subscriber sub = new Subscriber(); //订阅者对象
pub.BtnPressEvent += sub.ProcBtnPress; //4、订阅事件(或者说给事件添加处理程序)
pub.ButtonPress(); //5、触发事件
}
5、事件触发
在示例中,我们在发布者类型中,写了一个事件触发的方法。事件触发其实就是在某个需要的地方,调用该事件,内部会依次执行委托中的方法。
//发布者类型
class Publisher
{
//2、声明一个事件成员变量
public event DelButton BtnPressEvent;
//3、事件触发方法
public void ButtonPress()
{
Button button1 = new Button();
if (BtnPressEvent != null)
{
BtnPressEvent(button1, 10);
}
}
}
然后在外部调用事件触发方法。
void Main()
{
Publisher pub = new Publisher(); //发布者对象
Subscriber sub = new Subscriber(); //订阅者对象
pub.BtnPressEvent += sub.ProcBtnPress; //4、订阅事件(或者说给事件添加处理程序)
pub.ButtonPress(); //5、触发事件
}
标准事件的使用
上面说到BCL中有一个EventHandler的委托,专门用于系统事件,它是.Net框架提供的标准模式。
//sender: 发起事件的对象的引用,类型是object可以兼容所有类型,但是使用的时候需要把object转成对应类型
//s: 触发事件传进来的参数基类引用
public delegate void EvnetHandler(object sender, EventArgs e);
第二个参数要注意, 类型EventArgs是参数基类类型, 并不能直接使用, 要传递参数必须要派生一个参数类继承自EventArgs用来传递参数。
EventArgs类的定义:
// System.EventArgs
using System;
using System.Runtime.CompilerServices;
[Serializable]
[TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
public class EventArgs
{
public static readonly EventArgs Empty = new EventArgs();
}
下面我们使用标准事件委托EventHandler和自定义参数类,来实现事件的使用
using EventDemo;
void Main()
{
CustomButton btn = new CustomButton(); //发布者对象
Subscriber sub = new Subscriber(); //订阅者对象
btn.BtnPressEvent += sub.ProcBtnPress; //订阅事件
btn.Click(); //事件触发
}
namespace EventDemo
{
//发布者类, 这里写了一个简单的自定义按钮类作为发布者
class CustomButton
{
//2、这里我们使用系统的标准事件委托来声明事件变量
//这里的格式是使用了泛型委托,将默认的EventArgs参数类替换成我们自定义的
public event EventHandler BtnPressEvent;
//自定义参数变量
private CustomButtonEventArgs buttonEventArgs = new CustomButtonEventArgs();
//3、事件触发方法
public void Click()
{
buttonEventArgs.Id = 10;
buttonEventArgs.Value = 255;
if (BtnPressEvent != null)
{
BtnPressEvent(this, buttonEventArgs);
}
}
}
//订阅者类
class Subscriber
{
//4、自定义事件处理程序(按钮按下处理)
//注意,这里的参数要和标准委托EventHandler保持一致
public void ProcBtnPress(object sender, EventArgs e)
{
Console.WriteLine($"The publisher Type is {sender.GetType().Name}");
CustomButtonEventArgs ea = e as CustomButtonEventArgs;
Console.WriteLine($"The eventArgs is {ea.Id} and {ea.Value}");
}
}
//1、自定义按钮的参数类
class CustomButtonEventArgs : EventArgs
{
//这里的参数可以存放一些外部的自定义数据
public int Id { get; set; }
public int Value { get; set; }
}
}//namespace