论C#中的委托与事件
在C#里,委托与事件类是两个不易理解的概念。
主要阐述对委托与事件的理解,同时结合Observer设计模式与.NET Framework规范,针对生活中的案例来辨析委托与事件的应用。
标签:委托;事件Observer设计模式;.NET Framework
C#中的委托类似于C++中的函数指针,功能却更多。
事件是在委托的基础上的一种结构,类似于委托的变量,在界面的控件中处处都有应用。
1 什么是委托
委托的申明格式:修饰符delegate 返回值数据类型委托名(形参列表)。
例如:Delegate int AbcDel(string s, bool b);是一个委托申明,每一个委托都有自己的签名,就是说AbcDel这个委托有string 和bool类型的形参,返回一个int类型数据,即具有这样的函数签名。
委托类似于函数指针,它能够引用函数,通过传递地址的机制完成。
委托是一个类,当对它实例化时,要提供一个引用函数,将其作为它构造函数的参数。
例如:private int AbcFun (string str, bool bln){},则可以把这个函数传给AbcDel的构造函数,因为它们签名一致。
AbcDel sd = new SomeDelegate(AbcFun),sd 引用了AbcFun,也就是说,AbcFun已被sd所登记注册,如果你调用sd,AbcFun这个函数即会被调用。
2 事件的理解
事件的申明格式:修饰符event 委托名事件名;
例如:public event AbcDel Boil;//AbcDel为委托名
Boil事件的声明与之前委托变量sd的声明唯一的区别是多了event关键字。
声明事件类似于声明一个委托类型的变量。
3 Observer设计模式
假设热水器系统由两部分组成:热水器、警报器,由不同厂商进行了组装。
热水器仅负责烧水;警报器在水烧开时发出警报,当水温超过95度,就发出警报。
我们需要应用委托与事件来模拟此过程。
Observer设计模式是为了定义对象间的一种一对多的依赖关系,以便于当一个对象的状态改变时,其他依赖于它的对象会被自动告知并更新。
Observer模式是一种松耦合的设计模式。
它包括两类对象:
Subject:监视对象,它包含其他对象感兴趣的内容。
热水器是一个监视对象,它包含temprature字段,当它>95时,会不断把数据发给监视它的对象。
Observer:监视者,它监视Subject,当Subject中的某件事发生时,会告知Observer,Observer即采取相应行动。
Observer就是警报器。
在本例中,事情发生的顺序如下:
警报器告诉热水器,它对它的温度感兴趣(注册)。
热水器知道后保留对警报器的引用。
热水器进行烧水这一动作,当水温超过95度时,通过对警报器的引用,自动调用警报器的MakeAlert()方法。
代码如下:
Heater类:
private int temperature;
public delegate void BoilHandler(int param);//声明委托
public event BoilHandler BoilEvent; //声明事件
public void BoilWater() { for (int i = 0; i 95) {if (BoilEvent != null) {//如果有对象注册
BoilEvent(temperature) ;} } }//调用所有注册对象的方法
Alarm类:public void MakeA lert(int param) {//…输出水温}
主函数主要代码如下:
heater.BoilEvent += alarm.MakeAlert;//注册方法
heater.BoilWater();//烧水,会自动调用注册过的方法
输出:Alarm:嘀嘀嘀,水已经96度了:
Alarm:嘀嘀嘀,水已经97度了:// ...
4 Net Framework中的委托与事件
上面的代码很好地完成了工作,但.Net Framework中事件模型有所不同,.Net Framework的编码规范如下:
(1)委托类型名称都以EventHandler结束;
(2)委托的原型定义:返回值void,接受两个输入参数:一个Object 类型,一个EventArgs类型(或继承自EventArgs,但类型名要以EventArgs结尾);
(3)事件的命名:委托去掉EventHandler之后剩余的部分。
委托声明原型中的Object类型的参数代表了Subject,即监视对象,在本例中是Heater。
回调函数(Alarm的MakeAlert)可以通过它访问触发事件的对象(Heater)。
EventArgs对象包含了Observer感兴趣的数据,即temperature。
上面这些不仅是为了编码规范,也使程序更灵活。
将热水器的引用传给警报器的方法,就可以直接访问热水器的温度、型号等。
按.Net Framework改写此例:
Heater类代码如下:
public string type = “RealFire 001”;//型号
public delegate void BoiledEventHandler(Object sender, BoliedEventArgs e);
public event BoiledEventHandler Boiled;
public class BoliedEventArgs : EventArgs { //定义BoliedEventArgs类,
public readonly int temperature;//传递给observer所感兴趣的信息
public BoliedEventArgs(int temperature) {this.temperature = temperature; }}
protected virtual void OnBolied(BoliedEventArgs e) {
if (Boiled != null) { Boiled(this, e); // 调用所有注册对象的方法} }
public void BoilWater() {
for (int i = 0; i 95) {
BoliedEventArgs e = newBoliedEventArgs(temperature);OnBolied(e); }}}
Alarm类代码如下:
public void MakeAlert(Object sender, Heater.BoliedEventArgs e) {
Heater ht = (Heater)sender;Console.WriteLine(“Alarm:{0},嘀嘀嘀,水已经{1}度
了!”, ht.type,ht.temperature) ;}//访问公共字段
主函数除了事件名有所改变,其它代码同上。
输出为:Alarm:RealFire 001,嘀嘀嘀,水已经96度了!//省略..
5 结束语
在本文中我首先说明了我对委托与事件的理解。
接下来通过一个热水器系统的范例,结合Observer设计模式中与委托、事件,用代码实现了这个例子,随后结合此例讲述了.Net Framework中委托、事件的实现方式。
参考文献
[1]谭浩强.C++语言程序设计[M].北京:清华大学出版社,2006.
[2](美)Stephen C.Perry 著,肖斌,王小振等译.C#和.NET核心技术[M]. 北京:机械工业出版社,2007,(3).。