標籤:style blog http color 使用 os io for
前言:CLR事件模式建立在委託的基礎上,委託說調用回調方法的一種型別安全的方式。
我個人覺得事件本質就是委託,所以把委託弄清楚,只要知道事件基本文法就會使用了(如果說到安全執行緒,我個人覺得這個應該和線程一起去討論),所以這篇只做一個簡單的時間介紹和寫一些我看到的或我用到的一些代碼。
EventHandler
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代碼(這份代碼也是我的大愛,可以集中管理起來事件,不會讓事件到處亂飛,喜歡的朋友可以研究下,這裡就不多做介紹了,提供的代碼還是有不少錯誤,比如null 指標,沒有判斷是否存在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 class TypeWithLotsOfEvents { // Define a private instance field that references a collection. // The collection manages a set of Event/Delegate pairs. // NOTE: The EventSet type is not part of the FCL, it is my own type. private readonly EventSet m_eventSet = new EventSet(); // The protected property allows derived types access to the collection. protected EventSet EventSet { get { return m_eventSet; } } #region Code to support the Foo event (repeat this pattern for additional events) // Define the members necessary for the Foo event. // 2a. Construct a static, read-only object to identify this event. // Each object has its own hash code for looking up this // event’s delegate linked list in the object’s collection. protected static readonly EventKey s_fooEventKey = new EventKey(); // 2d. Define the event’s accessor methods that add/remove the // delegate from the collection. public event EventHandler<FooEventArgs> Foo { add { m_eventSet.Add(s_fooEventKey, value); } remove { m_eventSet.Remove(s_fooEventKey, value); } } // 2e. Define the protected, virtual On method for this event. protected virtual void OnFoo(FooEventArgs e) { m_eventSet.Raise(s_fooEventKey, this, e); } // 2f. Define the method that translates input to this event. public void SimulateFoo() { OnFoo(new FooEventArgs()); } #endregion #region Code to support the Bar event // 3. Define the members necessary for the Bar event. // 3a. Construct a static, read-only object to identify this event. // Each object has its own hash code for looking up this // event’s delegate linked list in the object’s collection. protected static readonly EventKey s_barEventKey = new EventKey(); // 3d. Define the event’s accessor methods that add/remove the // delegate from the collection. public event EventHandler<BarEventArgs> Bar { add { m_eventSet.Add(s_barEventKey, value); } remove { m_eventSet.Remove(s_barEventKey, value); } } // 3e. Define the protected, virtual On method for this event. protected virtual void OnBar(BarEventArgs e) { m_eventSet.Raise(s_barEventKey, this, e); } // 3f. Define the method that translates input to this event. public void SimulateBar() { OnBar(new BarEventArgs()); } #endregion}
View Code
關於Action和Func本質還是一樣的只是帶上了參數。
Action只有輸入參數,有好多重載的版本
Func有輸入參數,也有一個輸出參數,同樣有很多重載的版本
用一下就知道怎麼玩了,本質完全一樣的。
若有不對,不足之處請指出,請不要唯寫一個:漏洞百出此類評價,謝謝大家的指點和協助!