.Net 事件类型的实现和推荐做法
首先来看一下我们常见的自定义事件实现方式,首先创建可能的事件参数
1 /// <summary>
2 /// 事件参数
3 /// </summary>
4 public sealed class DemoEventArgs:EventArgs {
5
6 /// <summary>
7 /// 获取或设置事件的上下文
8 /// </summary>
9 public string Context {
10 get { return context; }
11 set { context = value; }
12 }
13 private string context;
14
15
16 }
当然,如果在实际使用中不需要事件参数的话,可以直接使用EventArgs.Empty静态字段。2 /// 事件参数
3 /// </summary>
4 public sealed class DemoEventArgs:EventArgs {
5
6 /// <summary>
7 /// 获取或设置事件的上下文
8 /// </summary>
9 public string Context {
10 get { return context; }
11 set { context = value; }
12 }
13 private string context;
14
15
16 }
紧接着我们需要定义事件成员
1
/// <summary>
2
/// 事件成员
3
/// </summary>
4
public event EventHandler<DemoEventArgs> NewEv
然后,我们就需要引发这个事件
/// <summary>2
/// 事件成员3
/// </summary>4
public event EventHandler<DemoEventArgs> NewEv 1 public void InvokeNewEvent() {
2 DemoEventArgs e = new DemoEventArgs();
3 e.Context = "Hello world";
4 this.OnNewEvent(e);
5 }
6
7 protected virtual void OnNewEvent(DemoEventArgs e) {
8 if (NewEvent!=null) {
9 NewEvent(this, e);
10 }
11 }
2 DemoEventArgs e = new DemoEventArgs();
3 e.Context = "Hello world";
4 this.OnNewEvent(e);
5 }
6
7 protected virtual void OnNewEvent(DemoEventArgs e) {
8 if (NewEvent!=null) {
9 NewEvent(this, e);
10 }
11 }
对于上面的代码,我们通过Reflector工具可以看到,对于事件定义
public event EventHandler<DemoEventArgs> NewEvent;
编译程序会将其翻译成两个public方法。

相应代码如下
1
[MethodImpl(MethodImplOptions.Synchronized)]
2
public void add_NewEvent(EventHandler<DemoEventArgs> value)
3
{
4
this.NewEvent = (EventHandler<DemoEventArgs>) Delegate.Combine(this.NewEvent, value);
5
}
6
7
[MethodImpl(MethodImplOptions.Synchronized)]
8
public void remove_NewEvent(EventHandler<DemoEventArgs> value)
9
{
10
this.NewEvent = (EventHandler<DemoEventArgs>) Delegate.Remove(this.NewEvent, value);
11
}
12
13
14
15
16
[MethodImpl(MethodImplOptions.Synchronized)]2
public void add_NewEvent(EventHandler<DemoEventArgs> value)3
{4
this.NewEvent = (EventHandler<DemoEventArgs>) Delegate.Combine(this.NewEvent, value);5
}6

7
[MethodImpl(MethodImplOptions.Synchronized)]8
public void remove_NewEvent(EventHandler<DemoEventArgs> value)9
{10
this.NewEvent = (EventHandler<DemoEventArgs>) Delegate.Remove(this.NewEvent, value);11
}12

13
14

15
16

MethodImpOption枚举类型定义了方法是如何被执行的。Synchronized指定了同时只能由一个线程执行该方法。静态方法锁定类型,而实例方法锁定实例。这样做的目的是,保证在操作实例事件时,对于每一个对象,在同一时刻add和remove方法的线程安全。
在类型上,所有事件的add和remove方法都将使用相同的锁。这样造成,在多个线程同时对不同事件进行订阅和撤销的时候,就会出现性能损失。在MSDN上,我们可以查阅到最后由一个注意事项:“实例或类型上的锁定(如同使用
线程同步的指导方针是不应该在对象本身上加同步锁,因为同步锁将对所有的代码公开,这意味着任何人都有可能蓄意的编写代码Lock这个对象,造成其它线程死锁。
为此,在大多数情况下上述情况并不可能发生,但是对于一个完美而稳固的组件来说,这就显得相当重要。鉴于以上原因,我们重新定义事件的实现。
1
public class EventDemo {
2
/// <summary>
3
/// 私有同步锁
4
/// </summary>
5
private readonly object _eventLock = new object();
6
7
/// <summary>
8
/// 事件成员
9
/// </summary>
10
private event EventHandler<DemoEventArgs> internalNewEvent;
11
public event EventHandler<DemoEventArgs> NewEvent {
12
add {
13
lock (_eventLock) {
14
internalNewEvent += value;
15
}
16
}
17
remove {
18
lock (_eventLock) {
19
internalNewEvent -= value;
20
}
21
}
22
}
23
24
protected virtual void OnNewEvent(DemoEventArgs e) {
25
//出于线程考虑,委托字段保存到临时字段中
26
EventHandler<DemoEventArgs> t = internalNewEvent;
27
if (t!=null) {
28
internalNewEvent(this, e);
29
}
30
}
31
32
public void InvokeNewEvent() {
33
DemoEventArgs e = new DemoEventArgs();
34
e.Context = "Hello world";
35
this.OnNewEvent(e);
36
}
37
38
39
40
}
public class EventDemo {2
/// <summary>3
/// 私有同步锁4
/// </summary>5
private readonly object _eventLock = new object(); 6

7
/// <summary>8
/// 事件成员9
/// </summary>10
private event EventHandler<DemoEventArgs> internalNewEvent;11
public event EventHandler<DemoEventArgs> NewEvent {12
add {13
lock (_eventLock) {14
internalNewEvent += value;15
}16
}17
remove {18
lock (_eventLock) {19
internalNewEvent -= value;20
}21
}22
}23

24
protected virtual void OnNewEvent(DemoEventArgs e) {25
//出于线程考虑,委托字段保存到临时字段中26
EventHandler<DemoEventArgs> t = internalNewEvent;27
if (t!=null) {28
internalNewEvent(this, e);29
}30
}31

32
public void InvokeNewEvent() {33
DemoEventArgs e = new DemoEventArgs();34
e.Context = "Hello world";35
this.OnNewEvent(e);36
}37

38
39

40
}再次查看编译程序最终生成的运行时代码
1
public void add_NewEvent(EventHandler<DemoEventArgs> value)
2
{
3
lock (this._eventLock)
4
{
5
this.internalNewEvent = (EventHandler<DemoEventArgs>) Delegate.Combine(this.internalNewEvent, value);
6
}
7
}
8
9
public void remove_NewEvent(EventHandler<DemoEventArgs> value)
10
{
11
lock (this._eventLock)
12
{
13
this.internalNewEvent = (EventHandler<DemoEventArgs>) Delegate.Remove(this.internalNewEvent, value);
14
}
15
}
16
17
18
19
20
public void add_NewEvent(EventHandler<DemoEventArgs> value)2
{3
lock (this._eventLock)4
{5
this.internalNewEvent = (EventHandler<DemoEventArgs>) Delegate.Combine(this.internalNewEvent, value);6
}7
}8

9
public void remove_NewEvent(EventHandler<DemoEventArgs> value)10
{11
lock (this._eventLock)12
{13
this.internalNewEvent = (EventHandler<DemoEventArgs>) Delegate.Remove(this.internalNewEvent, value);14
}15
}16

17
18

19
20

从性能上考虑,当某个组件定义大量事件时,该实例会浪费大量的内存,之所以是浪费,是因为其中很多事件都没有被订阅。这一点,我们可以参考System.Web.UI.Control中的实现。首先在内部包含了一个EventHandlerList类型的Events属性。
protected EventHandlerList Events
{
get
{
this.EnsureOccasionalFields();
if (this._occasionalFields.Events == null)
{
this._occasionalFields.Events = new EventHandlerList();
}
return this._occasionalFields.Events;
}
}
public event EventHandler DataBinding
{
add
{
this.Events.AddHandler(EventDataBinding, value);
}
remove
{
this.Events.RemoveHandler(EventDataBinding, value);
}
}引发事件
protected virtual void OnDataBinding(EventArgs e)
{
if (this.HasEvents())
{
EventHandler handler = this._occasionalFields.Events[EventDataBinding] as EventHandler;
if (handler != null)
{
handler(this, e);
}
}
}这样实现自我感觉不是很好,由于EventHandlerList是一个链表,造成通过内部的find方法查找时,要遍历循环。更理想的做法是使用一个Hashtable来存储委托链。
(参考:CLR via C#)



浙公网安备 33010602011771号