事件总结
1, 用法:
定义了事件成员的类型允许类型(或类型的实例)在某些特定事情发生的时候通知其他对象。因此定义一个事件成员意味了类型为我们提供了三种能力:
(1) 允许对象登记该事件
(2) 允许对象注销该事件
(3) 允许定义事件的对象维持一个登记对象的集合,并在某些特定的事情发生时通知这些对象
2, 比较
(1) 一开始觉得事件跟函数的调用很相似,甚至有时候觉得事件本来就跟函数调用一样,那又为什么要定义事件,而不直接用函数调用就好了呢?那当然,事件有他的独特之处,例如上面说到的三种能力。如果只是简单地调用函数,新的函数调用和函数的注销,就成了一个缺点,因为要经常改变函数的调用。而用事件成员的话,登记和注销事件,在客户端设定就可以了,比较灵活。
(2) 设计模式的观察模式和事件是不是有点相同呢?观察模式,主要的是创建一个对象池来保存注册了的对象,当主题发生变化的时候就通知所有的注册了的对象,并调用其各自的函数。而事件使用了委托来提供一种类型安全的方式调用回调方法。他们都是通过注册或登记,当发生特定动作后来通知其他对象。思路方面是一样的,而原理是不一样的。
3, 关联类以及实例代码
(1) 提供事件数据的类:名为EventNameEventArgs,其继承了System.EventArgs
(2) 事件委托:名为EventNameEventHandler
(3) 引发事件的类:该类必须提供事件声明和引发事件的方法
例如:
class MailManager //引发事件的类
{
public class MailMsgEventArgs : EventArgs //提供事件数据的类
{//1.传递给事件接受者的类型定义信息(定义附加信息)
public MailMsgEventArgs(string from, string to ,string subject,string body)
{
this.from = from;
this.to = to;
this.subject = subject;
this.body = body;
}
public readonly string from, to, subject, body;
}
//2.定义委托类型,第一个参数指向发送通知的对象,而第二个参数是继承EventArgs的类型,其中包含通知接受者需要的附加信息。
public delegate void MailMsgEventHandler(Object sender, MailMsgEventArgs args); //事件委托
//3,定义事件,其含义为所有事件通知的接受者都必须提供一个原型和MailMsgEventHanddler相匹配的回调方法。
public event MailMsgEventHandler MailMsg; //声明事件
//4,定义一个虚方法。是用来触发事件的
protected virtual void OnMailMsg(MailMsgEventArgs e) //引发事件的方法
{
if (MailMsg != null)
{
MailMsg(this, e);
}
}
//5,定义方法,当接收到邮件的时候就调用上面的虚方法
public void SimulateArrivingMsg(string from, string to, string subject, string body)
{
MailMsgEventArgs e = new MailMsgEventArgs(from,to,subject,body);
OnMailMsg(e);
}
}
4, 事件语句的编译
事件语句 public event MailMsgEventHandler MailMsg;通过编译器编译后会有3个构造:
(1) private MailMsgEventHandler MailMsg=null;
这个构造是一个委托类型字段,引用的是一个委托链表的首部。当有对象登记事件的时候,该字段就会指向一个MailMsgEventHandler委托实例,每个的委托实例内部都有一个指针指向另一个委托实例。
(2) [MethodImplAttribute(MethodImplOptions.Synchronized)]
Public void add_MailMsg(MailMsgEventHandler handler)
{
MailMsg=(MailMsgEventHandler)Delegate.Combine(MailMsg,handler);
}
这个是当对象登记事件的时候实行的操作,通过在委托链表上添加一个委托实例。
(3) [MethodImplAttribute(MethodImplOptions.Synchronized)]
Public void remove_MailMsg(MailMsgEventHandler handler)
{
MailMsg=(MailMsgEventHandler)Delegate.Remove(MailMsg,handler);
}
这个是注销对象,从委托链表中去掉某个委托实例的操作。
由上面事件语句的编译可以看到,事件是可以分为三部分来组成的。因此我们可以用上面代码来代替事件,换句话说就是显式来控制事件。代码如下:
public class MailManager2
{
public class MailMsgEventArgs //事件信息数据
{
public MailMsgEventArgs(string a)
{
this.a = a;
}
public readonly string a;
}
public delegate void MailMsgEventHandler(object sender, MailMsgEventArgs e);//委托原型方法
private MailMsgEventHandler mailmsgeventhandlerdelegate;//委托链表字段(不同点1)
public event MailMsgEventHandler mailmsg//事件(不同点2)
{
add
{
mailmsgeventhandlerdelegate=(MailMsgEventHandler)Delegate.Combine(mailmsgeventhandlerdelegate,value);//委托链表中添加委托实例
}
remove
{
mailmsgeventhandlerdelegate=(MailMsgEventHandler)Delegate.Remove(mailmsgeventhandlerdelegate,value);//委托链表中注销委托实例
}
}
public virtual void OnMailMsg(MailMsgEventArgs e)//虚方法
{
if (mailmsgeventhandlerdelegate != null)
{
mailmsgeventhandlerdelegate(this,e);//委托链表字段来进行事件的操作(不同点3),跟前面的不同
}
}
public void ReciveMessage(string a)
{
MailMsgEventArgs e = new MailMsgEventArgs(a);
OnMailMsg(e);
}
}
注意:1,上面的三个不同点,这就是隐式和显式的区别,同时显式控制事件的话,可不保证线程安全,因为这里没有特性MethodImplAttribute。
2,为什么要显式产生add和remove方法呢?一来编译器自动产生add和remove方法不够理想,例如如果我们需要频繁地添加或移除委托实例,同时我们又知道应用程序是在单线程环境下运行,这时再对包含委托实例的对象进行同步访问的话就会损伤应用程序的性能。二来就是当我们的类型定义了许多事件,而如果是编译器自动产生add和remove方法的话,就会导致每创建一个类型的实例都会因为事件就会创建所有事件的委托字段(本人估计,不知道对不对!!)。(解决方法如下)
5, 在一个类型中定义多个事件
通过创造性地显式实现事件add和remove方法,我们可以极大地减少对象由于定义多个事件而造成的内存浪费。怎么说呢?这里的原理就是让每个对象保存一个事件/委托对的集合。当一个新的对象被构造时候,该集合为空。当有事件被登记时,我们将在集合中查找该事件。如果集合中存在该事件,那么新的委托实例将被组合到表示该事件的委托链表上,否则该事件和新的委托实例将直接被添加到集合中。
当事件需要触发时,首先在集合中查找该事件,如果集合中不存在该事件,也就是说该事件没有登记,那么将没有任何委托实例被调用。如果集合中存在该事件,那么其对应的委托链表将被调用。