.Net 事件类型的实现和推荐做法
2007-08-15 14:41
429 查看
首先来看一下我们常见的自定义事件实现方式,首先创建可能的事件参数
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静态字段。
紧接着我们需要定义事件成员
1 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 }
对于上面的代码,我们通过Reflector工具可以看到,对于事件定义
public event EventHandler<DemoEventArgs> NewEvent;
编译程序会将其翻译成两个public方法。
[MethodImpl(MethodImplOptions.Synchronized)]
2public void add_NewEvent(EventHandler<DemoEventArgs> value)
3
7[MethodImpl(MethodImplOptions.Synchronized)]
8public void remove_NewEvent(EventHandler<DemoEventArgs> value)
9
13
14
15
16
MethodImpOption枚举类型定义了方法是如何被执行的。Synchronized指定了同时只能由一个线程执行该方法。静态方法锁定类型,而实例方法锁定实例。这样做的目的是,保证在操作实例事件时,对于每一个对象,在同一时刻add和remove方法的线程安全。
在类型上,所有事件的add和remove方法都将使用相同的锁。这样造成,在多个线程同时对不同事件进行订阅和撤销的时候,就会出现性能损失。在MSDN上,我们可以查阅到最后由一个注意事项:“实例或类型上的锁定(如同使用 Synchronized 标志一样)对于公共类型是不推荐使用的,其原因在于除了不是自己的代码的其他代码可对公共类型和实例采用锁定。这可能导致死锁或其他同步问题。 ”
线程同步的指导方针是不应该在对象本身上加同步锁,因为同步锁将对所有的代码公开,这意味着任何人都有可能蓄意的编写代码Lock这个对象,造成其它线程死锁。
为此,在大多数情况下上述情况并不可能发生,但是对于一个完美而稳固的组件来说,这就显得相当重要。鉴于以上原因,我们重新定义事件的实现。
1public void add_NewEvent(EventHandler<DemoEventArgs> value)
2
9public void remove_NewEvent(EventHandler<DemoEventArgs> value)
10
17
18
19
20
从性能上考虑,当某个组件定义大量事件时,该实例会浪费大量的内存,之所以是浪费,是因为其中很多事件都没有被订阅。这一点,我们可以参考System.Web.UI.Control中的实现。首先在内部包含了一个EventHandlerList类型的Events属性。
protected EventHandlerList Events
public event EventHandler DataBinding
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#)
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静态字段。
紧接着我们需要定义事件成员
1 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 }
对于上面的代码,我们通过Reflector工具可以看到,对于事件定义
public event EventHandler<DemoEventArgs> NewEvent;
编译程序会将其翻译成两个public方法。
[MethodImpl(MethodImplOptions.Synchronized)]
2public void add_NewEvent(EventHandler<DemoEventArgs> value)
3
7[MethodImpl(MethodImplOptions.Synchronized)]
8public void remove_NewEvent(EventHandler<DemoEventArgs> value)
9
13
14
15
16
MethodImpOption枚举类型定义了方法是如何被执行的。Synchronized指定了同时只能由一个线程执行该方法。静态方法锁定类型,而实例方法锁定实例。这样做的目的是,保证在操作实例事件时,对于每一个对象,在同一时刻add和remove方法的线程安全。
在类型上,所有事件的add和remove方法都将使用相同的锁。这样造成,在多个线程同时对不同事件进行订阅和撤销的时候,就会出现性能损失。在MSDN上,我们可以查阅到最后由一个注意事项:“实例或类型上的锁定(如同使用 Synchronized 标志一样)对于公共类型是不推荐使用的,其原因在于除了不是自己的代码的其他代码可对公共类型和实例采用锁定。这可能导致死锁或其他同步问题。 ”
线程同步的指导方针是不应该在对象本身上加同步锁,因为同步锁将对所有的代码公开,这意味着任何人都有可能蓄意的编写代码Lock这个对象,造成其它线程死锁。
为此,在大多数情况下上述情况并不可能发生,但是对于一个完美而稳固的组件来说,这就显得相当重要。鉴于以上原因,我们重新定义事件的实现。
1public void add_NewEvent(EventHandler<DemoEventArgs> value)
2
9public void remove_NewEvent(EventHandler<DemoEventArgs> value)
10
17
18
19
20
从性能上考虑,当某个组件定义大量事件时,该实例会浪费大量的内存,之所以是浪费,是因为其中很多事件都没有被订阅。这一点,我们可以参考System.Web.UI.Control中的实现。首先在内部包含了一个EventHandlerList类型的Events属性。
protected EventHandlerList Events
public event EventHandler DataBinding
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#)
相关文章推荐
- 从.NET迁移到.NET Core的一个主要原因,在于后者具备在Linux上运行的能力。但是对于大型企业应用,不可能实现一步迁移到位。由此,Microsoft推荐采用一种逐步迁移做法: 第一步,迁移到
- jquery实现上传文件大小类型的验证例子(推荐)
- 今天写页面,将.net也使用了语义法规来判断。做法来源是.net ado.net的类型判断 Infertype。一样的原理
- 一起谈.NET技术,从数据到代码—通过代码生成机制实现强类型编程[下篇]
- 复习一下 .Net: delegate(委托)、event(事件) 的基础知识,从头到尾实现事件!
- .NET事件传递的实现与事件链问题
- .Net中使用事件和委托实现Observer模式(一)
- 如何在.NET自定义控件中实现自己的事件(转)
- .Net/C# 封装磁盘目录文件搜索功能的工具类 (实现了与搜索相关的事件,以便插入客户处理代码)
- 实现基于事件的异步模式的最佳做法
- 推荐一个关于AOP的AOP@WORK的专栏作者的AOP专题系列文章,主要关于Java下的AOP实现,毕竟Java下的AOP实现确实要比.Net下成熟得多,共12篇
- .net实现松耦合事件的三种方法
- (转)实现基于事件通知的.Net套接字
- [推荐] 基于.net的Grid实现:Alchemi
- .Net/C# 实现真正的只读的 Hashtable 类型的属性 (ReadOnly Hashtable Property)
- [网络收集]给radio类型的INPUT添加客户端脚本 --附加实现JS来禁用onClick事件思路代码
- [.NET领域驱动设计实战系列]专题七:DDD实践案例:引入事件驱动与中间件机制来实现后台管理功能
- .Net 转战 Android 4.4 日常笔记(8)--常见事件响应及实现方式
- .NET中可空值类型【Nullable<T>】实现原理
- (nginx|apache)+tomcat 几种常见的代理实现方式及连接类型(connector type) 推荐