中文字幕日韩一区二区_国产一区二区av_国产毛片av_久久久久国产一区_色婷婷电影_国产一区二区精品

把事件當(dāng)作對象進(jìn)行傳遞

  最近在琢磨一些事情,和API設(shè)計(jì)有關(guān)。API設(shè)計(jì)在很多時(shí)候是和語言特性有關(guān)的,因此如Java這樣的語言,在API設(shè)計(jì)時(shí)會處處受到壓抑。而C#就能夠出現(xiàn)如MoqFluent NHIbernate這樣的項(xiàng)目。同樣,F(xiàn)#能夠開發(fā)出FsTestScala號稱Scalable Language,都是依靠著豐富的語言特性。不過,最近在使用C#的時(shí)候鼻子上也碰了一點(diǎn)灰,這是因?yàn)槲野l(fā)現(xiàn)“事件”這個東西沒法作為對象進(jìn)行傳遞。

public class Program{    public event EventHandler Submit;}

  我們?nèi)绻獮檫@個事件添加處理函數(shù)自然只要:

var myClass = new MyClass();myClass.Submit += (sender, eventArgs) => Console.WriteLine(sender);

  但是,如果我想寫一個“統(tǒng)一添加”的輔助函數(shù),例如可以這樣調(diào)用:

RegisterHandlers(myClass.Submit);

  就會發(fā)現(xiàn)——做不到。雖然,如果我們提供這樣的RegisterHandlers方法的實(shí)現(xiàn):

class Program{    public event EventHandler Submit;    static void RegisterHandlers(EventHandler ev)    {        ev += (sender, eventArgs) => Console.WriteLine("sender");    }    static void Main(string[] args)    {        Program p = new Program();        RegisterHandlers(p.Submit);        p.Submit("Hello World", EventArgs.Empty);    }}

  這是可以編譯通過的,似乎……應(yīng)該也過得去。但是實(shí)際執(zhí)行的時(shí)候就會發(fā)現(xiàn),p.Submit事件在觸發(fā)的時(shí)候依然會拋出NullReferenceException異常(為什么?)。因此,我們必須選擇另外一種方式。

  我們知道,雖說是一個事件,但是在注冊和移除處理函數(shù)的時(shí)候,實(shí)際上都是在調(diào)用add方法和remove方法。例如這句代碼:

myClass.Submit += (sender, eventArgs) => Console.WriteLine(sender);

  和下面的代碼其實(shí)是“等價(jià)”的:

 

myClass.add_Submit((sender, eventArgs) => Console.WriteLine(sender));

  “等價(jià)”打上引號是因?yàn)閍dd_Submit這行代碼其實(shí)無法編譯通過,我只是用來表示一個含義。但是這意味著,我們可以通過反射來調(diào)用add方法和remove方法。因此,我編寫了這樣的一個類:

public class Event{    public Event(Expression<Func> eventExpr)    {        ...    }    private object m_instance;    private MethodInfo m_addMethod;    private MethodInfo m_removeMethod;    public Event AddHandler(T handler)    {        this.m_addMethod.Invoke(this.m_instance, new object[] { handler });        return this;    }    public Event RemoveHandler(T handler)    {        this.m_removeMethod.Invoke(this.m_instance, new object[] { handler });        return this;    }}

  于是,我可以設(shè)法把一個事件封裝為一個對象:

class Program{    public event EventHandler Submit;    static void Main(string[] args)    {        Program p = new Program();        var ev = new Event<EventHandler>(() => p.Submit);        ev.AddHandler((sender, eventArgs) => Console.WriteLine(sender));        p.Submit("Hello World", EventArgs.Empty);    }}

  那么Event類的構(gòu)造函數(shù)該怎么寫呢?不過是解析表達(dá)式樹而已:

public Event(Expression<Func> eventExpr){    var memberExpr = eventExpr.Body as MemberExpression;    this.m_instance = memberExpr.Expression == null ? null :        Expression.Lambda<Func<object>>(memberExpr.Expression).Compile()();    var bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.InvokeMethod |        (this.m_instance == null ? BindingFlags.Static : BindingFlags.Instance);    var member = memberExpr.Member;    this.m_addMethod = member.DeclaringType.GetMethod("add_" + member.Name, bindingFlags);    this.m_removeMethod = member.DeclaringType.GetMethod("remove_" + member.Name, bindingFlags);}

  對于() => p.Submit這樣的代碼來說,它是一個MemberExpression,我們可以通過MemberExpression的屬性來說的p的實(shí)例。然后,根據(jù)Submit屬性的Member的Name便可以得出它的add或remove方法。其中需要再判斷這是一個實(shí)例事件還是一個靜態(tài)事件就可以了。總體來說,代碼比較簡單。當(dāng)然,在實(shí)際運(yùn)用中會要求在不合法的情況下拋出合適的異常。此外,如果您對性能有要求,也可以使用FastLambdaFastReflectionLib來提高性能。

  為了方便使用,我還為Event類重載了+和-兩個操作符,以及一個EventFactory類:

public static class EventFactory{    public static Event Create(Expression<Func> eventExpr)    {        return new Event(eventExpr);    }}public class Event{    ...    public static Event operator +(Event ev, T handler)    {        return ev.AddHandler(handler);    }    public static Event operator -(Event ev, T handler)    {        return ev.RemoveHandler(handler);    }}

  EventFactory類的Create方法可以避免顯式地提供T類型,而+和-操作符的目的便是在添加和刪除事件處理函數(shù)的時(shí)候“更像那么一回事”。于是現(xiàn)在我們便可以寫這樣的代碼:

class Program{    public event EventHandler Submit;    static void Main(string[] args)    {        Program p = new Program();        var ev = EventFactory.Create(() => p.Submit);        ev += (sender, eventArgs) => Console.WriteLine(sender);        p.Submit("Hello World", EventArgs.Empty);        Console.WriteLine("Press any key to exit...");        Console.ReadLine();    }}

  既然有了Event對象,我們便可以把它作為參數(shù)傳遞給其他方法,然后在其他的方法中添加或刪除事件處理函數(shù)。

  是不是挺美妙的?您也來下載完整代碼試試看吧,而且說不定……您還能發(fā)現(xiàn)這個方法里的一個陷阱。我承認(rèn),其實(shí)這個解決方案會遇見C#的一個問題,它糊弄了我,也糊弄了大家……

NET技術(shù)把事件當(dāng)作對象進(jìn)行傳遞,轉(zhuǎn)載需保留來源!

鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請第一時(shí)間聯(lián)系我們修改或刪除,多謝。

主站蜘蛛池模板: 日本亚洲精品成人欧美一区 | 欧美日韩亚洲一区二区 | 日本在线精品视频 | 中文字幕一区二区三区精彩视频 | 国产精品久久久久久52avav | 天天干天天操天天看 | 午夜欧美a级理论片915影院 | 日韩精品一区二区三区在线 | 日本黄色一级视频 | 色网站视频 | 黑人巨大精品欧美黑白配亚洲 | 爱爱爱av | 亚洲精品第一国产综合野 | 欧美激情在线精品一区二区三区 | 美女在线视频一区二区三区 | 91免费视频观看 | 国产精品欧美一区二区三区不卡 | 日韩一级电影免费观看 | 亚洲精品一区二区三区在线 | av黄色免费 | 伊人热久久| 精品亚洲国产成av人片传媒 | 亚洲福利在线观看 | 国产一区二区三区在线观看免费 | 日本中文在线 | 国产精品特级毛片一区二区三区 | 午夜在线| 日韩中文字幕免费在线观看 | 中文字幕一区二区三区不卡在线 | 99精品一区二区三区 | 九九久久99 | 亚洲在线 | 国产高清视频一区二区 | 日韩视频一区二区三区 | 三级黄视频在线观看 | 久久精品—区二区三区 | 国产aaaaav久久久一区二区 | 成人一区二区在线 | 免费影视在线观看 | 91av精品 | 最新中文字幕在线 |