.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靜態字段。
緊接著我們需要定義事件成員
1/**////?<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?????????}
對于上面的代碼,我們通過Reflector工具可以看到,對于事件定義
public?event?EventHandler<DemoEventArgs>?NewEvent;
編譯程序會將其翻譯成兩個public方法。
相應代碼如下
?1[MethodImpl(MethodImplOptions.Synchronized)]
?2public?void?add_NewEvent(EventHandler<DemoEventArgs>?value)
?3{
?4????this.NewEvent?=?(EventHandler<DemoEventArgs>)?Delegate.Combine(this.NewEvent,?value);
?5}
?6
?7[MethodImpl(MethodImplOptions.Synchronized)]
?8public?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????}
再次查看編譯程序最終生成的運行時代碼
?1public?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
?9public?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屬性。
????{
????????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#)
?
轉載于:https://www.cnblogs.com/DreamWinter/archive/2007/08/15/856666.html
總結
以上是生活随笔為你收集整理的.Net 事件类型的实现和推荐做法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ubuntu配置
- 下一篇: 用“已知”的办法解决“未知”的办法---