在大家学习与掌握.Net的过程中,理解委托与事件的概念,并恰当的使用它是跨越新手门槛额关键,下面结合我学习时的理解及几个经典的委托与事件的使用例子讲述一下。也算是对自己学习的一个总结。
委托与事件是实现观察者模式的一种非常好的工具,委托充当了抽象的Observer(观察者)的接口,提供事件的对象充当了目标对象。一个对象(目标对象)的状态发生改变,所有依赖对象(观察者对象)都将得到通知(TerryLee)。即当观察者中符合事件声明中委托的类型的方法订阅了目标对象的事件后。当目标对象事件触发时(由于状态改变),会执行观察者中相应的方法(即通知观察者对象)。
下面的例子来自于TerryLee的文章,李哥的代码经典,拿出来分析学习一下J。
using
System;
using
System.Collections.Generic;
using
System.Text;
namespace
ConsoleApplication1
{
class
Program
{
static
void
Main(
string
[] args)
{
//
目标对象
Stock stock
=
new
Stock(
"
Microsoft
"
,
120.00
);
//
观察者
Investor investor
=
new
Investor(
"
Jom
"
);
//
观察者订阅目标对象的事件
//
此处将观察者与目标对象关联了起来
stock.NotifyEvent
+=
new
NotifyEventHandler(investor.SendData);
//
此操作触发事件 -- 观察者会响应此事件
stock.Update();
Console.ReadLine();
}
}
public
delegate
void
NotifyEventHandler(
object
sender);
public
class
Stock
{
//
此处声明一个事件(省略了event关键字)
//
此为目标对象处
public
NotifyEventHandler NotifyEvent;
private
String _symbol;
private
double
_price;
public
Stock(String symbol,
double
price)
{
this
._symbol
=
symbol;
this
._price
=
price;
}
public
void
Update()
{
OnNotifyChange();
}
//
触发一个事件的操作
public
void
OnNotifyChange()
{
if
(NotifyEvent
!=
null
)
{
//
实际触发的操作 -- 在检测事件(委托对象)不为空后触发
//
传给事件(委托对象)的参数是目标对象,最终此对象被传递给观察者处理此事件的方法
NotifyEvent(
this
);
}
}
public
String Symbol
{
get
{
return
_symbol; } }
public
double
Price
{
get
{
return
_price; } }
}
//
观察者
public
class
Investor
{
private
string
_name;
public
Investor(
string
name)
{
this
._name
=
name;
}
//
观察者中在事件触发时,处理目标对象的函数
//
此处接受的参数为触发事件时传入的参数
public
void
SendData(
object
obj)
{
if
(obj
is
Stock)
{
Stock stock
=
(Stock)obj;
Console.WriteLine(
"
Notified {0} of {1}'s
"
+
"
change to {2:C}
"
, _name, stock.Symbol, stock.Price);
}
}
}
}
事件详解:
以下伴随一个示例说明定义一个事件的步骤:
示例来自C# via CLR,示例完成的功能:Fax,Pager两个对象订阅MailManager的事件。新邮件抵达时MailManager触发事件,MailManager将此通知送到订阅它的对象,这些对象以自己的方式处理邮件。
第一步:定义一个类型用于存放所有需要发送给事件通知接收者的附加信息
按照约定,所有传递给事件处理程序的用于存放事件信息的类都应该继承自System.EventArgs,并且类的名称应该以EventArgs结束。定义一个不需要任何额外信息的事件(如Button的Click事件)时,可以直接使用EventArgs.Empty,不用构建一个新的EventArgs类。
//
第一步:定义一个类型用于存放所有需要发送给事件通知接收者的附加信息
internal
class
NewMailEventArgs : EventArgs {
private
readonly
String m_from, m_to, m_subject;
public
NewMailEventArgs(String from, String to, String subject)
{
m_from
=
from;
m_to
=
to;
m_subject
=
subject;
}
public
String From
{
get
{
return
m_from; }
}
public
String To
{
get
{
return
m_to; }
}
public
String Subject
{
get
{
return
m_subject; }
}
}
第二步:定义事件成员
事件成员使用C#关键字event定义。
一个事件的定义包括以下部分:
代码
// 第二步: 定义事件成员
public event EventHandler<NewMailEventArgs> NewMail;
几个委托类型的约定定义方法:
-
sender参数的类型为Object
-
派生自EventArgs类的参数命名为e
-
事件处理程序的返回类型都为void
第三步:定义一个负责引发事件的方法来通知已订阅事件的对象事件已经发生
//
第步:定义一个负责引发事件的方法,来通知已订阅事件的对象已经发生。
protected
virtual
void
OnNewMail(NewMailEventArgs e) {
//
处于线程安全考虑,将委托字段保存到一个临时字段中
EventHandler
<
NewMailEventArgs
>
temp
=
NewMail;
//
通知所有已订阅事件的对象
if
(temp
!=
null
) temp(
this
, e);
}
//
定义了一个方法将输入转化为期望事件
public
void
SimulateNewMail(String from, String to, String subject) {
//
构建一个对象来存放我们希望传递给通知接收者的信息
NewMailEventArgs e
=
new
NewMailEventArgs(from, to, subject);
//
调用方法以通知对象事件已发生
OnNewMail(e);
监听事件的类型的设计
//
构建一个EventHandler<NewMailEventArgs>委托的实例,该委托指向FaxMsg回调方法
//
然后对MailManager的NewMail时间注册回调方法
mm.NewMail
+=
FaxMsg;
注以上代码内部使用了委托推断。
以下是注销MailManager的NewMail事件的代码
mm.NewMail
-=
FaxMsg;
说明:
事件触发时,订阅该事件的函数会自动执行。事件并不关心有多少函数订阅它。它只管发出这个事件被触发的通知。
//
当新的电子邮件到达时,MailManager将调用这个方法
private
void
FaxMsg(Object sender, NewMailEventArgs e) {
Console.WriteLine(
"
Faxing mail message:
"
);
Console.WriteLine(
"
From={0}, To={1}, Subject={2}
"
, e.From, e.To, e.Subject);
}
参考资料:
TerryLee:.NET设计模式(19):观察者模式(Observer Pattern)
C# via CLR(第二版)- 清华大学出版社