一区二区久久-一区二区三区www-一区二区三区久久-一区二区三区久久精品-麻豆国产一区二区在线观看-麻豆国产视频

從.NET中委托寫法的演變談開(kāi)去(上):委托與匿名方法

在《關(guān)于最近面試的一點(diǎn)感想》一文中,Michael同學(xué)談到他在面試時(shí)詢問(wèn)對(duì)方“delegate在.NET framework1.1,2.0,3.5各可以怎么寫”這個(gè)問(wèn)題。于是乎,有朋友回復(fù)道“請(qǐng)問(wèn)樓主,茴香豆的茴有幾種寫法”,“當(dāng)代孔乙己”,獨(dú)樂(lè),眾樂(lè)。看了所有的評(píng)論,除了某些朋友認(rèn)為“的確不該不知道這個(gè)問(wèn)題”之外,似乎沒(méi)有什么人在明確支持樓主。

不過(guò)我支持,為什么?因?yàn)槲乙蔡徇^(guò)出這樣的問(wèn)題。

這樣,我們暫且不提應(yīng)聘“高級(jí)開(kāi)發(fā)人員”的人,在“自稱熟悉各版本.NET框架”的前提下,是否應(yīng)該知道這個(gè)答案。我們也暫且不提Michael同學(xué)提問(wèn)的“目的”是什么。老趙就先單獨(dú)針對(duì)這個(gè)問(wèn)題進(jìn)行解釋,然后談?wù)勛约簽槭裁磿?huì)提出這個(gè)問(wèn)題吧。

可能有一件事情需要說(shuō)在前面,那就是:委托本身其實(shí)從來(lái)沒(méi)有改變過(guò),改變的一直都是委托的“寫法”。因此更確切地說(shuō),改變的只是“編譯器”。而本文所有內(nèi)容都用C#來(lái)實(shí)現(xiàn),其實(shí)談得也都是C#編譯器本身——但是其實(shí)VB.NET也有變化啊。再由于.NET版本和C#版本的關(guān)系也是非常密切的,因此全文就使用.NET版本進(jìn)行指代了。

.NET 1.x中委托的寫法

委托,如果不追究細(xì)節(jié),從表面上來(lái)看我們可以將其通俗地理解為一個(gè)安全的“函數(shù)指針”。當(dāng)然,這個(gè)函數(shù)指針其實(shí)也是一個(gè)對(duì)象,有自己的成員,也會(huì)封裝了被調(diào)用方的上下文等等。至于委托的定義和使用方式,則是這樣的:

public delegate int SomeDelegate(string arg1, bool arg2);public static int SomeMethod(string arg1, bool arg2) { return 0; }public class SomeClass{    public int SomeMethod(string a1, bool a2) { return 0; }    public event SomeDelegate SomeEvent;}static void Main(string[] args){    SomeClass someClass = new SomeClass();    SomeDelegate someDelegate = new SomeDelegate(someClass.SomeMethod);    someClass.SomeEvent += new SomeDelegate(SomeMethod);}

可見(jiàn),在.NET 1.x中需要使用new DelegateType(...)的方式來(lái)創(chuàng)建一個(gè)委托對(duì)象。不過(guò),作為委托對(duì)象內(nèi)部的方法它既可以是實(shí)例方法,也可以是靜態(tài)方法。此外,方法只需要匹配委托類型的簽名和返回值即可,方法參數(shù)的名稱不會(huì)成為約束。

嗯,就是這么簡(jiǎn)單。

.NET 2.0中委托的寫法

.NET中的委托引入了范型,且寫法略有簡(jiǎn)化:

public delegate TResult MyFunc<T1, T2, TResult>(T1 a1, T2 a2);public static int SomeMethod(string a1, bool a2) { return 0; }static void Main(string[] args){    MyFunc<string, bool, int> myFunc = SomeMethod;}

在.NET 2.0中,new DelegateType已經(jīng)可以省略,開(kāi)發(fā)人員可以直接將方法賦值給一個(gè)委托對(duì)象的引用。當(dāng)然,這個(gè)改進(jìn)不值一提,.NET 2.0中委托寫法的關(guān)鍵在于引入了“匿名方法”:

public static void TestRequest(string url){    WebRequest request = HttpWebRequest.Create(url);    request.BeginGetResponse(delegate(IAsyncResult ar)    {        using (WebResponse response = request.EndGetResponse(ar))        {            Console.WriteLine("{0}: {1}", url, response.ContentLength);        }    },    null);}

匿名方法,簡(jiǎn)單地說(shuō)就是內(nèi)聯(lián)在方法內(nèi)部的委托對(duì)象,它的關(guān)鍵便在于形成了一個(gè)閉包(委托執(zhí)行時(shí)所需的上下文)。如上面的代碼中,BeginGetResponse的第一個(gè)參數(shù)(委托)可以直接使用TestRequest方法的參數(shù)url,以及方法內(nèi)的“局部”變量request。如果沒(méi)有匿名函數(shù)這個(gè)特性的話,代碼寫起來(lái)就麻煩了,例如在.NET 1.x中您可能就必須這么寫:

展開(kāi)
折疊public static void TestRequest(string url){    WebRequest request = HttpWebRequest.Create(url);    object[] context = new object[] { url, request };    request.BeginGetResponse(TestAsyncCallback, context);}public static void TestAsyncCallback(IAsyncResult ar){     object[] context = (object[])ar.AsyncState;    string url = (string)context[0];    WebRequest request = (WebRequest)context[1];    using (WebResponse response = request.EndGetResponse(ar))    {        Console.WriteLine("{0}: {1}", url, response.ContentLength);    }}

此時(shí),我們往往會(huì)發(fā)現(xiàn),開(kāi)發(fā)人員需要花費(fèi)大量的精力,為一小部分代碼維護(hù)一大段上下文。例如在這段代碼中,我們會(huì)將url和request對(duì)象塞入一個(gè)object數(shù)組中,在回調(diào)函數(shù)中再通過(guò)危險(xiǎn)的Cast操作恢復(fù)數(shù)據(jù)。如果您希望“強(qiáng)類型”,那么只能為每個(gè)回調(diào)創(chuàng)建一個(gè)新的上下文對(duì)象,維護(hù)起來(lái)可能更加麻煩——要知道,在并行編程,異步調(diào)用越來(lái)越重要的今天,如果沒(méi)有匿名方法自動(dòng)保留上下文的特性,開(kāi)發(fā)人員會(huì)為這些“額外工作”疲于奔命的。

可能您會(huì)說(shuō),匿名方法的可讀性不佳,因?yàn)樾枰?ldquo;內(nèi)聯(lián)”。一個(gè)方法中內(nèi)聯(lián)太多,維護(hù)成本就上去了,所以匿名方法并不推薦使用。我想說(shuō)的是,您錯(cuò)了。如果為了可維護(hù)性,要將方法獨(dú)立拆開(kāi),也可以利用匿名方法的優(yōu)勢(shì):

public static void TestRequest(string url){    WebRequest request = HttpWebRequest.Create(url);    request.BeginGetResponse(delegate(IAsyncResult ar)    {        TestAsyncCallback(ar, request, url);    }, null);}public static void TestAsyncCallback(IAsyncResult ar, WebRequest request, string url){    using (WebResponse response = request.EndGetResponse(ar))    {        Console.WriteLine("{0}: {1}", url, response.ContentLength);    }}

如果借助.NET 3.5中的Lambda表達(dá)式,代碼可以寫的更簡(jiǎn)單易讀:

public static void TestRequest(string url){    WebRequest request = HttpWebRequest.Create(url);    request.BeginGetResponse(ar => TestAsyncCallback(ar, request, url), null);}

匿名方法的作用

千萬(wàn)不要小看匿名方法的作用,有些時(shí)候您認(rèn)為它的作用僅限于上文描述,只是因?yàn)闆](méi)有在某些問(wèn)題上踏前一步。例如,對(duì)于那些只需要“按需創(chuàng)建”,且要“線程安全”的對(duì)象,您會(huì)怎么做呢?沒(méi)錯(cuò),可以使用Double Check:

private object m_mutex = new object();private bool m_initialized = false;private BigInstance m_instance = null;public BigInstance Instance{    get    {        if (!this.m_initialized)        {            lock (this.m_mutex)            {                if (!this.m_initialized)                {                    this.m_instance = new BigInstance();                    this.m_initialized = true;                }            }        }        return this.m_instance;    }}

嗯,做的很漂亮!那么……這樣的屬性再來(lái)一個(gè),再來(lái)三個(gè),再來(lái)五個(gè)呢?可能有些朋友就會(huì)開(kāi)始大段地Copy & Paste,于是錯(cuò)誤便難免了。這里有一件真人真事,以前某位同學(xué)在一堆這樣的代碼中迷茫了,說(shuō)為什么用了這種方法,還是初始化了多次對(duì)象了?檢查了半天沒(méi)有看出問(wèn)題來(lái)。最后發(fā)現(xiàn),原因是訪問(wèn)了錯(cuò)誤的initialized變量(例如,在某個(gè)應(yīng)該訪問(wèn)artistInitialized的地方訪問(wèn)了articleInitialized)。可惜,大段時(shí)間已經(jīng)被浪費(fèi)了——更糟的是,心情也隨之變差了。

其實(shí),Copy & Paste很明顯沒(méi)有遵守DRY原則啊。為什么不把它們封裝在一處呢?例如:

展開(kāi)
折疊public class Lazy<T>{    public Lazy(Func<T> func)    {        this.m_initialized = false;        this.m_func = func;        this.m_mutex = new object();    }    private Func<T> m_func;    private bool m_initialized;    private object m_mutex;    private T m_value;    public T Value    {        get        {            if (!this.m_initialized)            {                lock (this.m_mutex)                {                    if (!this.m_initialized)                    {                        this.m_value = this.m_func();                        this.m_func = null;                        this.m_initialized = true;                    }                }            }            return this.m_value;        }    }}

于是,之前的代碼就可以簡(jiǎn)化成這樣了:

private Lazy<BigInstance> m_lazyInstance =    new Lazy<BigInstance>(delegate { return new BigInstance(); });public BigInstance Instance { get { return this.m_lazyInstance.Value; } }

還是太丑,上Lambda表達(dá)式!

private Lazy<BigInstance> m_lazyInstance =    new Lazy<BigInstance>(() => new BigInstance());public BigInstance Instance { get { return this.m_lazyInstance.Value; } }

如果沒(méi)有匿名方法,許多容易使用的編程模型和方式都難以開(kāi)展。例如,我們就不會(huì)有CacheHelper,也不會(huì)有AsyncTaskDispatcher(上),也很難利用“延遲”所帶來(lái)的便利,更難以出現(xiàn)微軟并行擴(kuò)展、CCR等優(yōu)秀框架。可以這么說(shuō),如果您不善于使用委托,您如果不知道如何合適地使用匿名方法,您在不自知的情況下可能就已經(jīng)編寫了大量額外的代碼了。

老趙平時(shí)的工作之一,便是為項(xiàng)目提供各種擴(kuò)展API,可以讓程序員們更愉快地進(jìn)行開(kāi)發(fā)工作,得到更好的生產(chǎn)力,讓代碼變得更加美好。如今C#有了匿名方法、Lambda表達(dá)式、表達(dá)式樹(shù)、擴(kuò)展方法等優(yōu)秀的語(yǔ)言特性,真讓我有“如魚(yú)得水”的感覺(jué)。因此,我對(duì)于Java這樣不思進(jìn)取的語(yǔ)言可以說(shuō)深惡痛絕(Java朋友們趕快學(xué)習(xí)Scala吧)。在看閱讀大量Java開(kāi)源項(xiàng)目代碼時(shí),我常有這樣的感覺(jué):“如果是C#的話,利用匿名方法,這個(gè)類不就可以不寫,那個(gè)類就可以省略……”。沒(méi)錯(cuò),為了保留回調(diào)函數(shù)的上下文而創(chuàng)建一些類,對(duì)于C#程序員來(lái)說(shuō),的確是一件有些不可思議的事情。

至于Lambda表達(dá)式以及其他話題,我們下次再說(shuō)吧。

匿名方法的缺點(diǎn)

匿名方法的優(yōu)勢(shì)在于自動(dòng)形成閉包,而它的缺點(diǎn)也是讓程序員“不自覺(jué)”地創(chuàng)建了閉包,這會(huì)讓某些對(duì)象的生命周期加長(zhǎng)。例如在一開(kāi)始的TestRequest方法中,表面上看起來(lái)url是參數(shù),request是局部變量,有些朋友可能會(huì)認(rèn)為它們?cè)诜椒ㄍ顺龊缶鸵呀?jīng)準(zhǔn)備回收了。不過(guò)因?yàn)樾纬闪碎]包,url和request已經(jīng)“升級(jí)”為一個(gè)對(duì)象的域變量,它的生命周期延長(zhǎng)了,延長(zhǎng)至回調(diào)函數(shù)執(zhí)行完畢。因此,一不注意可能就會(huì)產(chǎn)生一些莫名其妙的情況

其實(shí),這些都是“延遲”所帶來(lái)的陷阱,作為一個(gè)優(yōu)秀的開(kāi)發(fā)人員,除了知道某個(gè)東西的作用和優(yōu)勢(shì),也要知道它的問(wèn)題,不是嗎?

總結(jié)

您現(xiàn)在還覺(jué)得.NET中的“委托”是一個(gè)簡(jiǎn)單的,只適合“初學(xué)者”,一搜就都知道的概念嗎?

您可能會(huì)說(shuō)“是”,不過(guò)對(duì)我來(lái)說(shuō)這真不簡(jiǎn)單,我也是慢慢才意識(shí)到這些的。雖然關(guān)于委托的大量?jī)?nèi)容可以在互聯(lián)網(wǎng)上搜索而來(lái),但還是有太多東西是需要在大量編程實(shí)踐中積累下來(lái)的——等一下,這不就是“高級(jí)開(kāi)發(fā)人員”和“初學(xué)者”的主要區(qū)別之一嗎?

嘲笑孔乙己的朋友們,你們?cè)谝晃侗梢?ldquo;茴”的四種寫法的同時(shí),說(shuō)不定也失去了一個(gè)了解中國(guó)傳統(tǒng)文化的機(jī)會(huì)呢!

剩下的下次再說(shuō)吧,Lambda表達(dá)式還等著我們。

相關(guān)文章

  • 從.NET中委托寫法的演變談開(kāi)去(上):委托與匿名方法
  • 從.NET中委托寫法的演變談開(kāi)去(中):Lambda表達(dá)式及其優(yōu)勢(shì)
  • 從.NET中委托寫法的演變談開(kāi)去(下):性能相關(guān)

NET技術(shù)從.NET中委托寫法的演變談開(kāi)去(上):委托與匿名方法,轉(zhuǎn)載需保留來(lái)源!

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

主站蜘蛛池模板: 黄色在线免费观看网址 | 青青自拍视频一区二区三区 | 久久99中文字幕伊人 | 日本不卡一区二区三区视频 | 91在线公开视频 | 日本精品一区二区三区在线 | 国产精品亚洲高清一区二区 | 午夜精品国产 | 在线免费视频一区二区 | 国产另类视频 | 精品国产一区二区三区国产馆 | 一区二区视频在线观看免费的 | 国产亚洲欧洲精品 | 视色4se成人午夜精品 | 欧美欧美乱码一二三区 | 欧美日韩a| 国产美女网站 | 美女很黄很黄免费 | 亚洲吊丝网 | 4hu四虎永久免在线视 | 丁香六月在线 | 久久精品国产99久久无毒不卡 | 亚洲不卡一区二区三区 | 亚洲一区二区三区精品国产 | 黄色小视频在线观看免费 | 在线看国产视频 | 欧美噜噜噜 | 色播影院性播免费看 | 久久精品8 | 久久老色鬼天天综合网观看 | 国产精品免费一区二区三区四区 | 国产精品欧美激情在线播放 | 91在线免费视频 | 91杏吧在线网站 | 91视频网址 | 91亚洲免费视频 | 欧美一级高跟鞋鞋交 | 天天干天天操天天舔 | 久久综合九色综合91 | 91婷婷| 欧美一级日韩一级亚洲一级 |