再说说C#定义事件的写法

 
  
public event EventHandler Started; protected virtual void OnStarted(EventArgs e) { EventHandler handler = Started; if (handler != null ) { handler( null , e); } }

像上面这段代码,在很多地方都可以看到,几乎可说是创建事件的标准写法了。这段代码最主要的功能是防止调用订阅者列表为空的委托。但是其中有两处让人第一眼看上去感到疑惑的地方,至少我不是一开始就理解的。

 

首先是显眼的protected virtual. 相信搞过WinForm编程的都很熟悉,因为从WinForm控件继承后可以看到大量的如OnClick(EventArgs), OnKeyDown(KeyEventArgs)这样的保护虚方法。用Reflector看System.Windows.Forms.Control,可以发现OnClick(EventArgs)方法定义如下:

 
  
[EditorBrowsable(EditorBrowsableState.Advanced)] protected virtual void OnClick(EventArgs e) { EventHandler handler = (EventHandler) base .Events[EventClick]; if (handler != null ) { handler( this , e); } }

这和前面看到的定义是一致的,可见.NET中正是遵循了这个写法。从这个方法在WinForm中的应用,也就能看出这样写的意义了。声明为protected可以让派生类自由触发事件,而virtual则令派生类可以在改变事件触发时的行为,比如在事件触发前后进行一些操作,或者决定是否要触发事件。

 

其次是handler的定义,看起来handler是一个冗余变量,完全多此一举。而且违反了重构中的“尽量不使用局部变量”的原则。我一开始甚至做过手动inline这样的代码中的handler。然而,事实上在多线程环境中这一行代码是必不可少的,因为可能在handler != null 判断通过后,Started在另一个线程中被设为了null! 因此,将Started首先赋给一个局部变量,避免了外界环境的影响。

 

但是还没有完,上面说的都是理论,如果我说,这样的代码明显是会被编译器优化掉的,因此这样写完全没有意义,怎样呢?毕竟EventHandler作为一个委托,并没有用volatile关键字声明(事实上事件不能声明为volatile,但可以在这里用Thread.VolatileRead(ref object)方法),使用时也没有用Interlocked来访问。我其实真没有想到那么远,不过CLR Via C#上给出了解释(记不得是哪一章了):JIT的编译器在这里会识别出这个写法并且确保不会把handler变量优化掉。真是万幸,但估计又成为了一个被学院派的诟病的特性。不过管他呢,让我们少写一些代码难道不是好事么,至少我觉得很好。

 

另外值得一提的是,如果只是很简单的类,并且预料到不会有什么继承形式(最好同时标记为sealed),而不想写出那一坨代码的话(tip: 输invoke插入snipplet可以稍微减轻一点负担),可以采用下面一种写法偷偷懒:

 
  
public event EventHandler Started = delegate { };

转载于:https://www.cnblogs.com/Gildor/archive/2011/01/18/1937886.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值