一、何為多事件多委托
在講述本節的只是之前,我們先要理解什么是事件、什么是委托。
委托:如果是從事過C/C++開發的可以理解為一個函數指針。當然你看我的文章一定不是為了這個已經被濫用慣的說法,但是大家一定都知道變量有各種類型,有整數型、浮點型、字符型等等。但是我們經常使用的類型中還有一個是方法(也叫函數),既然我們可以使用,也可以聲明,那就應該存在一種可以保存這種變量的類型,在C#中就是委托。稍有不同的是沒有一種通用的類型,不同類型的方法(函數)需要我們去定義不同的委托(委托其實就是定義了一種新的類型),並且可以初始化,賦值,甚至可以直接調用這個方法。
事件:就是使用委托這個技術去專門做一方面的事。這個事就是將環境產生的各種事件對應到不同方法,從而方便程序員可以根據當前的事件去管理應用程序。事件其實就是定義一個委托變量,然后定義一個特性去管理這個委托變量。這樣的好處是其他人無法調用這個事件,也方面管理。
通過上面的基礎,現在我們就可以講述我們今天所需要學習的知識,在我們開發的時候不知有沒有遇到過一個事件需要多個委托去響應它,甚至可能是多個事件需要多個委托去響應,其中的關系是多對多中間還包含兩個事件需要同一個委托去響應。
而這篇文章將會用很簡短的時間去管理這些事件。
二、思路來源
本想研究ASP.NET事件的原理,但是后來無意中發現了這個有趣的東西。
在查看 HtmlButton 類的反編譯的代碼后發現一個方法的實現步驟:

當我看到這個之后,我就寫了一個 button 標簽並且設置 runat='server' 屬性,如下:

其次為了可以看到變化,我也拖了一個 Label 控件在頁面中用來反饋結果。
然后就是在后台編寫測試的代碼:

可能會有人很奇怪,為什么 Page_Load 中的代碼不放在 IsPostBack 中,這里需要說明下,開始我也是放在IsPsotBack中的,
但是當你點擊按鈕,頁面回傳后,對應的事件並沒有執行,一開始我認為是這個控件的原因,就是使用了Button服務器控件,但是后來
的結果還是一樣的,經過后來的測試發現其實並沒有錯,只是在它保存這些事件后,並沒有存放在任何位置,自然導致的后果是頁面回傳
后這些信息都不會存在了。所以不能放在IsPostBack中。
解決了點擊不響應的問題之后,我們點擊按鈕看看,結果兩個事件都執行了,並且是按照我們賦給ServerClick變量的順序執行的,執行后的結果:

這就已經證明了,單個事件可以對應多個委托作為響應,並且響應的順序是按照我們賦給變量的順序執行的。唯一的缺點是沒有持久化數據。
三、繼續挖掘
上面的測試證明了我的想法,自然就要繼續研究更深的。所以我們繼續去查看是什么類型的變量。
通過一層一層的找,經過它的父類 HtmlContainerControl 類、HtmlControl 類再到Control 類,我們才發現了定義這個變量的類型:

既然已經找到了變量,我們自然就順藤摸瓜的去找到它的屬性:

發現並沒有什么我們想要的,所以我們可以直接看這個EventHandlerList類了:

其中對於我們比較主要的就是這個 AddHandler 方法,還有 this[] 方法:

這里我們可以清楚的看到在我們獲得這個我們賦給它的委托后,返回的委托僅僅只是 EventHandler ,但是卻響應了兩個事件委托。原因是 EventHandler 類中其實已經存儲了這兩個委托。
(細心的同志一定發覺到這里使用了單向鏈表——筆者感覺是模擬了棧)
四、實戰使用
為了能夠快速的看到如何使用我們將不通過頁面的事件來調用,而是自己手動調用:
前台:
1 <form id="form1" runat="server"> 2 <div> 3 <button id="button1" runat="server">Click Me</button> 4 </div> 5 <div> 6 <asp:Label ID="Label1" runat="server" Text=""></asp:Label> 7 </div> 8 </form>
后台:
1 private EventHandlerList _events = new EventHandlerList(); 2 3 /// <summary> 4 /// key 5 /// </summary> 6 private static readonly object CustonEvent = new object(); 7 8 /// <summary> 9 /// 事件一 10 /// </summary> 11 public EventHandler CustomEvent; 12 13 /// <summary> 14 /// 事件二 15 /// </summary> 16 public EventHandler CustomEvent1; 17 18 protected void Page_Load(object sender, EventArgs e) 19 { 20 if (!IsPostBack) 21 { 22 CustomEvent = Button1_Click_1; 23 CustomEvent1 = Button1_Click_2; 24 _events.AddHandler(CustonEvent, CustomEvent); 25 _events.AddHandler(CustonEvent, CustomEvent1); 26 EventHandler handler = (EventHandler)_events[CustonEvent]; 27 handler(null, null); 28 } 29 } 30 31 protected void Button1_Click_1(object sender, EventArgs e) 32 { 33 Label1.Text += "<br /> Click_1 Click"; 34 } 35 36 protected void Button1_Click_2(object sender, EventArgs e) 37 { 38 Label1.Text += "<br /> Click_2 Click"; 39 }
這里我們先給EventHandlerList類型的變量中模擬增加一個事件對應多個委托,
然后提取出委托並直接運行,會發現最后的結果是兩個委托都執行了,並且是按照我們賦值的順序運行的。
五、EventHandlerList 的全部代碼
1 [HostProtection(SecurityAction.LinkDemand, SharedState=true)] 2 public sealed class EventHandlerList : IDisposable 3 { 4 // Fields 5 private ListEntry head; 6 private Component parent; 7 8 // Methods 9 [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] 10 public EventHandlerList() 11 { 12 } 13 14 [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] 15 internal EventHandlerList(Component parent) 16 { 17 this.parent = parent; 18 } 19 20 public void AddHandler(object key, Delegate value) 21 { 22 ListEntry entry = this.Find(key); 23 if (entry != null) 24 { 25 entry.handler = Delegate.Combine(entry.handler, value); 26 } 27 else 28 { 29 this.head = new ListEntry(key, value, this.head); 30 } 31 } 32 33 public void AddHandlers(EventHandlerList listToAddFrom) 34 { 35 for (ListEntry entry = listToAddFrom.head; entry != null; entry = entry.next) 36 { 37 this.AddHandler(entry.key, entry.handler); 38 } 39 } 40 41 public void Dispose() 42 { 43 this.head = null; 44 } 45 46 private ListEntry Find(object key) 47 { 48 ListEntry head = this.head; 49 while (head != null) 50 { 51 if (head.key == key) 52 { 53 return head; 54 } 55 head = head.next; 56 } 57 return head; 58 } 59 60 public void RemoveHandler(object key, Delegate value) 61 { 62 ListEntry entry = this.Find(key); 63 if (entry != null) 64 { 65 entry.handler = Delegate.Remove(entry.handler, value); 66 } 67 } 68 69 // Properties 70 public Delegate this[object key] 71 { 72 get 73 { 74 ListEntry entry = null; 75 if ((this.parent == null) || this.parent.CanRaiseEventsInternal) 76 { 77 entry = this.Find(key); 78 } 79 if (entry != null) 80 { 81 return entry.handler; 82 } 83 return null; 84 } 85 set 86 { 87 ListEntry entry = this.Find(key); 88 if (entry != null) 89 { 90 entry.handler = value; 91 } 92 else 93 { 94 this.head = new ListEntry(key, value, this.head); 95 } 96 } 97 } 98 99 // Nested Types 100 private sealed class ListEntry 101 { 102 // Fields 103 internal Delegate handler; 104 internal object key; 105 internal EventHandlerList.ListEntry next; 106 107 // Methods 108 public ListEntry(object key, Delegate handler, EventHandlerList.ListEntry next) 109 { 110 this.next = next; 111 this.key = key; 112 this.handler = handler; 113 } 114 } 115 } 116 117 118 Collapse Methods 119
提醒:如果需要持久化保存請保存至Session中,視圖狀態無法保存。
