淺析CLR的事件

文章目錄:

     一、C#(.net framework框架)中的事件以及特色
程序員

   二、事件的組成部分windows

   三、編輯器如何實現事件的安全

        四、顯式實現事件框架

一、C#(.net framework框架)中的事件以及特色

    CLR事件模型以委託爲基礎。使用委託來調用回調方法。聲明方式使用event關鍵字。 事件能夠理解爲在CLR中使用event關鍵字修飾的一個委託實例。編輯器

    事件的特色以下所示:ide

   1)、方法能登記對事件關注this

     2)、方法能註銷對事件的關注spa

   3)、事件發生時,登記了的方法將收到通知.net

二、事件的組成部分

  1)、定義類型容納全部須要發送給事件通知接收者的附加信息(事件要傳遞的參數,我是這麼理解的)線程

   
 1     /// <summary>
 2     /// 發送給事件接收者的附加信息
 3     /// </summary>
 4     public class DailyEventArgs : EventArgs
 5     {
 6         private readonly string _title;
 7         private readonly string _body;
 8         public DailyEventArgs(string title,string body)
 9         {
10             this._title = title;
11             this._body = body;
12         }
13         public string Title { get {return _title; } }
14         public string Body { get { return _body; } }
15     }
發送給事件接收者的附加信息

       這裏解釋下EventArgs,它由FCL中定義,根據CLR事件約定,全部事件參數均要繼承自這個類,且事件的參數類名要以EventArgs結尾。它的定義以下:

  2)、定義事件成員

  事件成員使用event關鍵字修飾。咱們須要定義事件的可訪問級別(默認都是public);委託類型(要調用的方法的原型)。

   
1 class DailiesEventDemo
2     {
3         //也能夠這麼定義
4         //public delegate void CustomDelegate(object sender, DailyEventArgs e);
5         //public event CustomDelegate CustomDaily;
6 
7         public event EventHandler<DailyEventArgs> NewDaily;
8 
9     }
事件成員

  NewDaily是事件的名稱,這裏使用系統提供的委託EventHandlerEventHandler的定義以下所示,因此咱們能夠接受的方法原型爲void MethodName(Object sender,DailyEventArgs e);

  

  3)、定義負責引起事件的方法來通知事件的的登記對象

  按照CLR的約定,咱們須要定義一個受保護的虛方法。引起事件時,調用該方法。方法的參數爲咱們定義的DailyEventArgs對象,方法的默認實現是檢查是否有對象登記了對事件的關注,假若有對象登記對事件的關注,引起事件通知登記對象。

   
1         protected virtual void OnDendDaily(DailyEventArgs e)
2         {
3             //爲了線程安全,將委託字段存入一個臨時變量中
4             EventHandler<DailyEventArgs> temp = Volatile.Read(ref NewDaily);
5             if (temp != null)
6                 temp.Invoke(this, e);
7         }
定義負責引起事件的方法

  4)、定義方法將輸入轉化爲指望事件

   
 1         /// <summary>
 2         /// 定義方法將輸入轉化爲指望事件
 3         /// </summary>
 4         /// <param name="title"></param>
 5         /// <param name="body"></param>
 6         public void SimulateNewDaily(string title,string body)
 7         {
 8             DailyEventArgs dailyEventArgs = new DailyEventArgs(title, body);
 9             OnDendDaily(dailyEventArgs);
10         }
定義方法將輸入轉化爲指望事件

      而後讓咱們定義兩個關注類,來登記下事件(C# 用+= 來登記對事件的關注,-=註銷對事件的關注),具體代碼以下:

  
 1     public class ZhangSan
 2     {
 3         public void ObtainNewDaily(object sender, DailyEventArgs e)
 4         {
 5             Console.WriteLine($"張三獲取一份報紙,標題是{e.Title},內容是{e.Body}");
 6         }
 7     }
 8     public class LiSi
 9     {
10         public void ObtainNewDaily(object sender, DailyEventArgs e)
11         {
12             Console.WriteLine($"李四獲取一份報紙,標題是{e.Title},內容是{e.Body}");
13         }
14     }
15         static void Main(string[] args)
16         {
17             try
18             {
19                 DailiesEventDemo dailiesEventDemo = new DailiesEventDemo();  
20                 ZhangSan zhangSan = new ZhangSan();
21                 dailiesEventDemo.NewDaily += zhangSan.ObtainNewDaily; //張三登記註冊
22                 LiSi liSi = new LiSi();
23                 dailiesEventDemo.NewDaily += liSi.ObtainNewDaily;   //李四登記註冊
24                 dailiesEventDemo.SimulateNewDaily("人民日報", "程序員的崛起");
25                 dailiesEventDemo.NewDaily -= liSi.ObtainNewDaily;   //李四取消登記註冊
26                 dailiesEventDemo.SimulateNewDaily("新華日報", "How find a gril friend for Procedures MonKey ");
事件執行

三、編輯器實現事件的方式

    咱們用ILSpy來反編譯下咱們生成的程序集。

 能夠看到,public event EventHandler<DailyEventArgs> NewDaily; 被編譯器翻譯成了一個具備add 和remove方法的對象,add 和remove方法中的 EventHandler<DailyEventArgs> eventHandler = this.NewDaily;拿到當前事件(被event修飾的委託)對象,而後調用 Delegate.Combine() 和 Delegate.Remove()方法添加或者移除委託連中的委託。

四、顯式實現事件

上面能夠看到,每個事件都會生成一個委託的實例字段(例如上面的NewDaily),在winform程序中,窗體控件(system.windows.forms.controller)具備70多個事件,爲此咱們須要準備70多個委託字段,然而咱們常常用到的事件也就幾個而已,這樣就致使了大量的委託實例字段被建立,從而浪費大量內存。 好在咱們能夠顯示實現事件:

 1 public sealed class EventKey { }
 2     public sealed class EventSet {
 3         //私有字典維護EventKey => delegate 的映射
 4         private readonly Dictionary<EventKey, Delegate> m_events = new Dictionary<EventKey, Delegate>();
 5 
 6         //添加EventKey => delegate映射 或者將委託和現有的EventKey合併
 7         public void Add(EventKey eventKey,Delegate handler)
 8         {
 9             Monitor.Enter(m_events);
10             Delegate d;
11             m_events.TryGetValue(eventKey, out d);
12             m_events[eventKey] = Delegate.Combine(d, handler);
13             Monitor.Exit(m_events);
14         }
15 
16         //從EvetnKey 刪除委託,而且在刪除最後一個委託時候刪除映射關係
17         public void Remove(EventKey eventKey, Delegate handler)
18         {
19             Monitor.Enter(m_events);
20             Delegate d;
21             if(m_events.TryGetValue(eventKey, out d))
22             {
23                 d = Delegate.Remove(d, handler);
24                 if (d != null)
25                     m_events[eventKey] = d;
26                 else
27                     m_events.Remove(eventKey);
28             }
29             Monitor.Exit(m_events);
30         }
31         public void Raise(EventKey eventKey,Object sender,EventArgs e)
32         {
33             Delegate d;
34             Monitor.Enter(m_events);
35             m_events.TryGetValue(eventKey, out d);
36             Monitor.Exit(m_events);
37             if(d!=null)
38             {
39                 d.DynamicInvoke(new Object[] { sender, e });
40             }
41         }
42     }
43     public class FooEventArgs : EventArgs { }
44     public class TryWithLostOfEvents
45     {
46         //定義私有實例字段來引用集合
47         //集合用於管理一組 事件/委託 對
48         //EventSet類型不是否是FCL的一部分,他是我本身的類型
49         private readonly EventSet m_eventSet = new EventSet();
50         //受保護屬性識破愛生類能訪問集合
51         protected EventSet EventSet { get { return m_eventSet; } }
52         // 支持Foo事件的代碼,定義Foo事件的必要成員,構造靜態制度對象標識事件;每一個對象都有本身的哈希碼,一邊在對象的集合中查找這個事件的委託鏈表
53         protected static readonly EventKey s_fooEventKey = new EventKey();
54         //定義訪問器方法,用於集合中CRUD
55         public event EventHandler<FooEventArgs> Foo
56         {
57             add { m_eventSet.Add(s_fooEventKey, value); }
58             remove { m_eventSet.Remove(s_fooEventKey, value); }
59         }
60         //調用事件的虛方法
61         protected virtual void OnFoo(FooEventArgs e)
62         {
63             m_eventSet.Raise(s_fooEventKey,this, e);
64         }
65         //定義將輸入轉換成這個事件的方法
66         public void SimulateFoo() { OnFoo(new FooEventArgs()); }
67     }
事件的顯式實現

   來運行一下

1                 TryWithLostOfEvents tryWithLostOfEvents = new TryWithLostOfEvents();
2                 tryWithLostOfEvents.Foo += TryWithLostOfEvents_Foo;  //TryWithLostOfEvents_Foo登記對事件的訂閱
3                 tryWithLostOfEvents.Foo += fun1;                     //  fun1登記對事件的訂閱
4                 tryWithLostOfEvents.SimulateFoo();
5                 tryWithLostOfEvents.Foo -= fun1;                    //  fun1註銷對事件的訂閱
6                 tryWithLostOfEvents.SimulateFoo();
運行顯示定義事件

相關文章
相關標籤/搜索