前言:CLR事件模式建立在委托的基礎上,委托說調用回調方法的一種類型安全的方式。
我個人覺得事件本質就是委托,所以把委托弄清楚,只要知道事件基本語法就會使用了(如果說到線程安全,我個人覺得這個應該和線程一起去討論),所以這篇只做一個簡單的時間介紹和寫一些我看到的或我用到的一些代碼。
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
上面是C#的源碼,超簡單有木有(還是委托)。
有兩個類型:
1.Object sender :事件源
2.TEventArgs e :泛型,這里可以自定義事件所需要的額外參數。
既然知道基本的語法,我們來看看怎么寫。

internal sealed class NewMailEventArgs : EventArgs { PRivate readonly String m_from, m_to, m_subject; public NewMailEventArgs(String from, String to, String subject) { m_from = from; m_to = to; m_subject = subject; } public String From { get { return m_from; } } public String To { get { return m_to; } } public String Subject { get { return m_subject; } }}View Code這里我們定義另一個類NewMailEventArgs,注意了 這里繼承了EventArgs,所有的事件參數的類都需要繼承這個父類。這里都是我們回調函數所要用的參數。

internal class MailManager { public event EventHandler<NewMailEventArgs> NewMail; protected virtual void OnNewMail(NewMailEventArgs e) { if (NewMail!=null) NewMail(this, e); } }View Code這里定義了一個事件NewMail,EventHandler的參數類型是NewMailEventArgs(就是我們自定義的那個參數類),我們慣用的方式在同一個類里面寫調用的方法,以On開頭+事件名結尾,但是這里有一個很危險地方,就是多線程的時候,如果在執行的時候,其他地方正好取消了訂閱呢,又會變成NULL,所以這里可以變成

internal class MailManager { public event EventHandler<NewMailEventArgs> NewMail; protected virtual void OnNewMail(NewMailEventArgs e) { e.Raise(this, ref NewMail); } public void SimulateNewMail(string from, string to, string subject) { var e = new NewMailEventArgs(from, to, subject); OnNewMail(e); } } public static class EventArgExtensions { public static void Raise<TEventArgs>(this TEventArgs e, Object sender, ref EventHandler<TEventArgs> eventDelegate) where TEventArgs : EventArgs { EventHandler<TEventArgs> temp = Interlocked.CompareExchange(ref eventDelegate, null, null); if (temp != null) temp(sender, e); } }View Code這里是我習慣用法,也是CLR書上推薦的用法,做一個通用Args的擴展類EventArgExtensions:主要說用于做線程安全和執行回調函數所用(我覺得這樣比較單一,封裝起來,要修改也只要修改一處就好,記住這里是通用的事件調用,所以如果有特殊的需求,請不要加進來,可以在特殊的地方處理),有關Interlocked.CompareExchange可以看下官方的文檔,這個就是用來做輕量級線程同步比較的。

static void Main(string[] args) { var mail = new MailManager(); mail.NewMail += mail_NewMail; mail.SimulateNewMail("a", "b", "c"); mail.NewMail -= mail_NewMail; Console.ReadKey(); } static void mail_NewMail(object sender, NewMailEventArgs e) { Console.WriteLine(e.From); Console.WriteLine(e.To); Console.WriteLine(e.Subject); }View Code最后我們在Main方法中訂閱事件(mail.NewMail+=mail_NewMail;這里的話直接+=然后按兩個Tab鍵,自己就出來了),取消訂閱用-=。是不是很簡單?到這里基本的事件就已經說完了。
接下來分析一下CLR最后提供一份EventSet代碼(這份代碼也是我的大愛,可以集中管理起來事件,不會讓事件到處亂飛,喜歡的朋友可以研究下,這里就不多做介紹了,提供的代碼還是有不少錯誤,比如空指針,沒有判斷是否存在key之類的情況,不過里面的想法的確值得好好學習)

public sealed class EventKey : Object {}///////////////////////////////////////////////////////////////////////////////public sealed class EventSet { // The private dictionary used to maintain EventKey -> Delegate mappings private readonly Dictionary<EventKey, Delegate> m_events = new Dictionary<EventKey, Delegate>(); // Adds an EventKey -> Delegate mapping if it doesn't exist or // combines a delegate to an existing EventKey public void Add(EventKey eventKey, Delegate handler) { Monitor.Enter(m_events); Delegate d; m_events.TryGetValue(eventKey, out d); m_events[eventKey] = Delegate.Combine(d, handler); Monitor.Exit(m_events); } // Removes a delegate from an EventKey (if it exists) and // removes the EventKey -> Delegate mapping the last delegate is removed public void Remove(EventKey eventKey, Delegate handler) { Monitor.Enter(m_events); // Call TryGetValue to ensure that an exception is not thrown if // attempting to remove a delegate from an EventKey not in the set Delegate d; if (m_events.TryGetValue(eventKey, out d)) { d = Delegate.Remove(d, handler); // If a delegate remains, set the new head else remove the EventKey if (d != null) m_events[eventKey] = d; else m_events.Remove(eventKey); } Monitor.Exit(m_events); } // Raises the event for the indicated EventKey public void Raise(EventKey eventKey, Object sender, EventArgs e) { // Don't throw an exception if the EventKey is not in the set Delegate d; Monitor.Enter(m_events); m_events.TryGetValue(eventKey, out d); Monitor.Exit(m_events); if (d != null) { // Because the dictionary can contain several different delegate types, // it is impossible to construct a type-safe call to the delegate at // compile time. So, I call the System.Delegate type’s DynamicInvoke // method, passing it the callback method’s parameters as an array of // objects. Internally, DynamicInvoke will check the type safety of the // parameters with the callback method being called and call the method. // If there is a type mismatch, then DynamicInvoke will throw an exception. d.DynamicInvoke(new Object[] { sender, e }); } }}View Code
public class FooEventArgs : EventArgs { }// Define the EventArgs-derived type for this event.public class BarEventArgs : EventArgs { }///////////////////////////////////////////////////////////////////////////////internal c
新聞熱點
疑難解答