重温Observer模式--热水器·改 这两篇文章让我弄懂了委托、事件和观察者模式的基础知识,另外深入的事件文章还有博客堂 破宝的事件三部曲,(btw 这些都是我看过的,如果你见见过更好的文章请跟帖以便更多人学习,谢谢。:))
现在来说下这个被说烂了的东东我感觉需要注意的地方。
1 单播和多播事件
2 事件的显式定义(继而解释委托和事件的区别)
3 .net事件模型
对于单播和多播事件概念。查资料是这么定义的:单播事件就是对象(类)发出的事件通知,只能被外界的某一个事件处理程序处理,而不能被多个事件处理程序处理,多播事件是对象(类)发出的事件通知,可以同时被外界不同的事件处理程序处理。
说是这么简单,理解清楚不是很简单。有没有想过这里是到底怎么实现的呢?这里我读过《.net框架程序设计》第17.5 委托史话:System.Delegate与system.MulticastDelegate,如果有兴趣可以找来看看,system.MulticastDelegate定义于FCL继承自System.Delegate,这里MulticastDelegate其实就是多播委托,那么多播事件也是通过这个实现的,不用说Delegate大家都可以猜到是单播委托了,那么平时我们定义一个委托public delegate void Back(Object value, Int32 item, Int32 numItems)
当编译器遇到这句委托定义,会产生一个完整的类定义:
{
public Back(Object target, Int32 methodPtr);
public void virtual Invoke(Object value, Int32 item, Int32 numItems);
public virtual IAsyncResult BeginInvoke(Object vlaue ,Int32. numItems, AsyncCallback callback,Object object);
public virtual void EndInvoke(IAsyncResult result);
}
事件的显式定义,也许你还不知道显式定义是怎么回事,相信很多朋友平时自己定义事件也没注意过这个问题。
回忆下平时我们是怎么定义事件的呢?是不是下面的样子:
{
//定义一个委托类
public delegate void MailMesgEventHandler(Object sender, EventArgs e);
//定义对应委托的事件
public event MailMesgEventHandler MailMsg;
}
看了很简单,是的,这里就是隐式定义事件,为什么叫隐式呢,我自己弄的名字哈哈,编译这句事件定义代码时要产更多的代码,就像下面这些简化的伪码:
public void add_MailMesg(MailMesgEventHandler handler)
{
MailMesg = (MailMesgEventHandler);
Delegate.Combine(MailMsg, handler);
}
public void remove_MailMesg(MailMesgEventHandler handler)
{
MailMesg = (MailMesgEventHandler);
Delegate.Remove(MailMsg, handler);
}
private MailMesgEventHandler MailMesg = null;
public void add_MailMesg(MailMesgEventHandler handler)
{
MailMesg = (MailMesgEventHandler);
Delegate.Combine(MailMsg, handler);
}
public void remove_MailMesg(MailMesgEventHandler handler)
{
MailMesg = (MailMesgEventHandler);
Delegate.Remove(MailMsg, handler);
}
待会我们谈显式定义事件的时候,你会更确定事件就是委托的属性!
看来是时候说显式定义事件啦,先看看下面的代码:
private MailMesgEventHandler MailMesg;
public event MailMesgeventHandler MailMsg
{
add
{
MailMesg = (MailMesgEventHandler);
Delegate.Combine(MailMsg, handler);
}
remove
{
MailMesg = (MailMesgEventHandler);
Delegate.Remove(MailMsg, handler);
}
}
public delegate void MailMesgEventHandler(Object sender, EventArgs e);
private MailMesgEventHandler MailMesg;
public event MailMesgeventHandler MailMsg
{
add
{
MailMesg = (MailMesgEventHandler);
Delegate.Combine(MailMsg, handler);
}
remove
{
MailMesg = (MailMesgEventHandler);
Delegate.Remove(MailMsg, handler);
}
}
我们可以控制add/remove内部的逻辑,这样可以在+=(其实就是add,这里是操作符重载)和-=(其实就是remove)时更灵活。
最后一个概念是.net事件模型,说事件模型之前先考虑一个问题,我们不管是隐式还是显式定义事件最后都要定义一个委托字段,大家知道System.Windows.Forms.Control类型中大约60个事件,如果Control类型在实现这些事件的时候让编译器自动产生委托字段以及add和remove访问器方法,那么每个Control类型将仅仅因为事件就有将近60个委托,
由于我们大多数时候在对象上登记的事件都很少,因此没创建一个Control类型(以及继承自Control的类型)的实例都会很浪费内存,顺便说下System.Web.UI.Control类型也存在这样的问题。(以上斜体字是载自《.net框架程序设计修订版》238-239页)
那么我们怎么做能可以既定义事件又同时省去这些委托字段呢?
先看个图:
这是个美妙的想法,同时可以各种委托有一个相同的“老窝”,而每个委托的工作又不相互干扰。
其实微软就给我们提供了这样的家,它就是System.ComponetModel.EventHandlerList
那么至于里面怎么实现的我是不知道,不过现在我们可以自己动手做个委托“老窝”。(注下面代码全部载自《.net框架程序设计修订版,》)
1
using System;2
using System.Collections.Generic;3
using System.Text;4
using System.Collections;5
using System.Runtime.CompilerServices;6

7
namespace BaoBaoCore.Event8


{9

/**//// <summary>10
/// 事件集合类11
/// 公用类似微软System.ComponentModel.EventHandlerList12
/// 速度比EventHandlerList快(因为其采用链表,而本类用的是hashtable)13
/// </summary>14
public class EventHandlerSet : IDisposable15

{16
//用于保存“事件键/委托值”对的私有散列表17
private Hashtable _events = new Hashtable();18

19
//一个索引器,用于获取或设置与传入的事件对象的20
//散列键相关联的委托21
public virtual Delegate this[Object eventKey]22

{23
//如果对象不在集合中,则返回null24
get25

{26
return (Delegate)_events[eventKey];27
}28

29
set30

{31
_events[eventKey] = value;32
}33
}34

35
//在指定的事件对象的散列键对应的委托链表上添加/组合一个委托实例36
public virtual void AddHandler(Object eventKey, Delegate handler)37

{38
_events[eventKey] = Delegate.Combine((Delegate)_events[eventKey], handler);39
}40

41
//在指定的事件对象的散列键对应的委托链表上移除一个委托实例42
public virtual void RemoveHandler(Object eventKey, Delegate handler)43

{44
_events[eventKey] = Delegate.Remove((Delegate)_events[eventKey], handler);45
}46

47
//在指定的事件对象的散列键对应的委托链表上触发事件48
public virtual void Fire(Object eventKey, Object sender, EventArgs e)49

{50
Delegate d = (Delegate)_events[eventKey];51
if (d != null)52

{53

d.DynamicInvoke(new Object[]
{ sender, e });54
}55
}56

57
//方法声明来源于IDisposable接口58
//释放对象以使散列表占用的内存资源在下一次垃圾59
//收集中被回收,从而阻止垃圾收集器提升其代价60
public void Dispose()61

{62
_events = null;63
}64

65
//下面的静态方法返回一个对传入的EventHandlerSet66
//对象的线程安全的封装67
public static EventHandlerSet Synchronized(EventHandlerSet eventHandlerSet)68

{69
if (null == eventHandlerSet)70

{71
throw new ArgumentNullException("eventHandlerSet");72
}73
return new SynchronizedEventHandlerSet(eventHandlerSet);74
}75

76
//下面的类在EventHandlerSet基础上提供了77
//一个线程安全的封装78
private class SynchronizedEventHandlerSet : EventHandlerSet79

{80
//引用非线程安全的对象81
private EventHandlerSet _eventHandlerSet;82

83
//在非线程安全的对象上构造一个线程安全的封装84
public SynchronizedEventHandlerSet(EventHandlerSet eventHandlerSet)85

{86
this._eventHandlerSet = eventHandlerSet;87

88
//释放基类中的散列表对象89
Dispose();90
}91

92
//线程安全的索引器93
public override Delegate this[object eventKey]94

{95
[MethodImpl(MethodImplOptions.Synchronized)]96
get97

{98
return _eventHandlerSet[eventKey];99
}100

101
[MethodImpl(MethodImplOptions.Synchronized)]102
set103

{104
_eventHandlerSet[eventKey] = value;105
}106
}107

108
//线程安全的AddHandler方法109
[MethodImpl(MethodImplOptions.Synchronized)]110
public override void AddHandler(object eventKey, Delegate handler)111

{112
_eventHandlerSet.AddHandler(eventKey, handler);113
}114

115
//线程安全的RemoveHandler方法116
[MethodImpl(MethodImplOptions.Synchronized)]117
public override void RemoveHandler(object eventKey, Delegate handler)118

{119
_eventHandlerSet.RemoveHandler(eventKey, handler);120
}121

122
//线程安全的Fire方法123
[MethodImpl(MethodImplOptions.Synchronized)]124
public override void Fire(object eventKey, object sender, EventArgs e)125

{126
_eventHandlerSet.Fire(eventKey, sender, e);127
}128
}129
} 130
}131

1
using System;2
using System.Collections.Generic;3
using System.ComponentModel;4
using System.Data;5
using System.Drawing;6
using System.Text;7
using System.Windows.Forms;8

9
using BaoBaoCore.Event;10
namespace WindowsSample11


{12
public partial class Form1 : Form13

{14
//定义一个受保护的实例字段,改字段引用一个集合来管理一组事件/委托对。15
private EventHandlerSet _workEventSet = EventHandlerSet.Synchronized(new EventHandlerSet());16

17
//构造一些只读且受保护的事件静态key18
protected static readonly Object WorkStartEventKey = new Object();19
protected static readonly Object WorkEndEventKey = new Object(); 20

21
//为WorkStart事件定义继承自EventArgs的类型22
public class WorkStartEventArgs : EventArgs23

{24

字段#region 字段25
private DateTime _workStartTime;26
private string _message = "";27
#endregion28

29

属性#region 属性30

/**//// <summary>31
/// 事件相关字串信息32
/// </summary>33
public string Message34

{35
get 36

{37
return _message;38
}39
}40

41

/**//// <summary>42
/// 启动时间43
/// </summary>44
public DateTime WorkStartTime45

{46
get47

{48
return _workStartTime;49
}50
}51
#endregion52

53

构造方法#region 构造方法54

/**//// <summary>55
/// 构造方法56
/// </summary>57
/// <param name="message">事件信息</param>58
/// <param name="workStartTime">启动时间</param> 59
public WorkStartEventArgs(string message, DateTime workStartTime)60

{61
_message = message;62
_workStartTime = workStartTime;63
}64
#endregion65
}66

67
//为WorkEnd事件定义继承自EventArgs的类型68
public class WorkEndEventArgs : EventArgs69

{70

字段#region 字段71
private DateTime _workEndTime;72
private string _message = "";73
#endregion74

75

属性#region 属性76

/**//// <summary>77
/// 事件相关字串信息78
/// </summary>79
public string Message80

{81
get82

{83
return _message;84
}85
}86

87

/**//// <summary>88
/// 启动时间89
/// </summary>90
public DateTime WorkEndTime91

{92
get93

{94
return _workEndTime;95
}96
}97
#endregion98

99

构造方法#region 构造方法100

/**//// <summary>101
/// 构造方法102
/// </summary>103
/// <param name="message">事件信息</param>104
/// <param name="workStartTime">结束时间</param> 105
public WorkEndEventArgs(string message, DateTime workEndTime)106

{107
_message = message;108
_workEndTime = workEndTime;109
}110
#endregion111
}112

113
//为事件定义委托原型114
public delegate void WorkStartEventHandler(Object sender, WorkStartEventArgs e);115
public delegate void WorkEndEventHandler(Object sender, WorkEndEventArgs e);116

117
//定义WorkStart访问器方法用于在集合上添加/移除委托实例118
public event WorkStartEventHandler WorkStart119

{120
add121

{122
_workEventSet.AddHandler(WorkStartEventKey, value);123
}124

125
remove126

{127
_workEventSet.RemoveHandler(WorkStartEventKey, value);128
}129
}130

131
//定义WorkEnd访问器方法用于在集合上添加/移除委托实例132
public event WorkEndEventHandler WorkEnd133

{134
add135

{136
_workEventSet.AddHandler(WorkEndEventKey, value);137
}138

139
remove140

{141
_workEventSet.RemoveHandler(WorkEndEventKey, value);142
}143
}144

145
//为唤醒OnWorkStart事件定义一个受保护的虚方法146
protected virtual void OnWorkStart(WorkStartEventArgs e)147

{148
_workEventSet.Fire(WorkStartEventKey, this, e);149
}150

151
//为唤醒OnWorkEnd事件定义一个受保护的虚方法152
protected virtual void OnWorkEnd(WorkEndEventArgs e)153

{154
_workEventSet.Fire(WorkEndEventKey, this, e);155
}156

157
public Form1()158

{159
InitializeComponent();160

161
//绑定自定义事件,通过结果可以看出先响应先添加的响应委托162
this.WorkStart += new WorkStartEventHandler(WorkStart1);163
this.WorkStart += new WorkStartEventHandler(WorkStart2);164

165
this.WorkEnd += new WorkEndEventHandler(WorkEnd2);166
this.WorkEnd += new WorkEndEventHandler(WorkEnd1);167
}168

169
private void button1_Click(object sender, EventArgs e)170

{171
OnWorkStart(new WorkStartEventArgs("工作开始啦!!", DateTime.Now));172
}173

174
private void button2_Click(object sender, EventArgs e)175

{176
OnWorkEnd(new WorkEndEventArgs("工作结束啦!!", DateTime.Now));177
}178

179
//事件相关响应方法180
private void WorkStart1(object sender, WorkStartEventArgs e)181

{182
MessageBox.Show(e.Message + " 被WorkStart1方法捕获到开始事件");183
}184

185
private void WorkStart2(object sender, WorkStartEventArgs e)186

{187
MessageBox.Show(e.WorkStartTime.ToString() + " 被WorkStart2方法捕获到开始事件");188
}189

190
private void WorkEnd1(object sender, WorkEndEventArgs e)191

{192
MessageBox.Show(e.Message + " 被WorkEnd1方法捕获到结束事件");193
}194

195
private void WorkEnd2(object sender, WorkEndEventArgs e)196

{197
MessageBox.Show(e.WorkEndTime.ToString() + " 被WorkEnd2方法捕获到结束事件");198
}199

200
201
}202
}好了到这里基本说完了,你看完了这篇事件的东东会不会再次说——都说破啦,哈哈。
代码下载
LoveBaoBao
作者:LoveBaoBao
出处:http://lovebaobao.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。








