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

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

在《關于最近面試的一點感想》一文中,Michael同學談到他在面試時詢問對方“delegate在.NET framework1.1,2.0,3.5各可以怎么寫”這個問題。于是乎,有朋友回復道“請問樓主,茴香豆的茴有幾種寫法”,“當代孔乙己”,獨樂,眾樂。看了所有的評論,除了某些朋友認為“的確不該不知道這個問題”之外,似乎沒有什么人在明確支持樓主。

不過我支持,為什么?因為我也提過出這樣的問題。

這樣,我們暫且不提應聘“高級開發(fā)人員”的人,在“自稱熟悉各版本.NET框架”的前提下,是否應該知道這個答案。我們也暫且不提Michael同學提問的“目的”是什么。老趙就先單獨針對這個問題進行解釋,然后談談自己為什么會提出這個問題吧。

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

.NET 1.x中委托的寫法

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

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);}

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

嗯,就是這么簡單。

.NET 2.0中委托的寫法

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

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)可以省略,開發(fā)人員可以直接將方法賦值給一個委托對象的引用。當然,這個改進不值一提,.NET 2.0中委托寫法的關鍵在于引入了“匿名方法”:

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);}

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

展開
折疊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);    }}

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

可能您會說,匿名方法的可讀性不佳,因為需要“內聯(lián)”。一個方法中內聯(lián)太多,維護成本就上去了,所以匿名方法并不推薦使用。我想說的是,您錯了。如果為了可維護性,要將方法獨立拆開,也可以利用匿名方法的優(yōu)勢:

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表達式,代碼可以寫的更簡單易讀:

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

匿名方法的作用

千萬不要小看匿名方法的作用,有些時候您認為它的作用僅限于上文描述,只是因為沒有在某些問題上踏前一步。例如,對于那些只需要“按需創(chuàng)建”,且要“線程安全”的對象,您會怎么做呢?沒錯,可以使用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;    }}

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

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

展開
折疊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;        }    }}

于是,之前的代碼就可以簡化成這樣了:

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

還是太丑,上Lambda表達式!

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

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

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

至于Lambda表達式以及其他話題,我們下次再說吧。

匿名方法的缺點

匿名方法的優(yōu)勢在于自動形成閉包,而它的缺點也是讓程序員“不自覺”地創(chuàng)建了閉包,這會讓某些對象的生命周期加長。例如在一開始的TestRequest方法中,表面上看起來url是參數(shù),request是局部變量,有些朋友可能會認為它們在方法退出后就已經(jīng)準備回收了。不過因為形成了閉包,url和request已經(jīng)“升級”為一個對象的域變量,它的生命周期延長了,延長至回調函數(shù)執(zhí)行完畢。因此,一不注意可能就會產生一些莫名其妙的情況

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

總結

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

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

嘲笑孔乙己的朋友們,你們在一味鄙視“茴”的四種寫法的同時,說不定也失去了一個了解中國傳統(tǒng)文化的機會呢!

剩下的下次再說吧,Lambda表達式還等著我們。

相關文章

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

NET技術從.NET中委托寫法的演變談開去(上):委托與匿名方法,轉載需保留來源!

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

主站蜘蛛池模板: 色视频在线播放 | 天堂av中文在线 | 国产精品一区二区视频 | 成人免费看片 | 一区精品国产欧美在线 | 久久久久久九九九九 | 色精品| 天天操天天玩 | 99热在线观看精品 | 精品国产一区二区在线 | 久久中文字幕一区 | 一区二区三区在线 | 欧 | 伊人免费网 | 亚洲va欧美va天堂v国产综合 | 黄色视频a级毛片 | 色婷婷在线视频 | 一级日批片 | 99国产在线 | 日韩精品一区二区三区久久 | 韩日一区 | 狠狠干2020 | 亚洲一区 中文字幕 | 日韩成人久久 | 天天干夜夜操视频 | 一区二区三区国产好 | 亚洲一区二区三区视频 | 久久久久久亚洲 | 久久99久久98精品免观看软件 | 欧美日韩亚洲国产 | a级在线免费视频 | 成人在线精品视频 | 青青久久久| 黄色片视频网站 | 超碰伊人久久 | 羞羞视频免费在线观看 | 日韩高清国产一区在线 | 日本网站免费观看 | 精品1区| 欧美啊v在线观看 | 国产欧美在线视频 | 伊人热久久 |