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

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

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

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

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

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

.NET 1.x中委托的寫法

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

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(...)的方式來創建一個委托對象。不過,作為委托對象內部的方法它既可以是實例方法,也可以是靜態方法。此外,方法只需要匹配委托類型的簽名和返回值即可,方法參數的名稱不會成為約束。

嗯,就是這么簡單。

.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已經可以省略,開發人員可以直接將方法賦值給一個委托對象的引用。當然,這個改進不值一提,.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);}

匿名方法,簡單地說就是內聯在方法內部的委托對象,它的關鍵便在于形成了一個閉包(委托執行時所需的上下文)。如上面的代碼中,BeginGetResponse的第一個參數(委托)可以直接使用TestRequest方法的參數url,以及方法內的“局部”變量request。如果沒有匿名函數這個特性的話,代碼寫起來就麻煩了,例如在.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);    }}

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

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

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

匿名方法的作用

千萬不要小看匿名方法的作用,有些時候您認為它的作用僅限于上文描述,只是因為沒有在某些問題上踏前一步。例如,對于那些只需要“按需創建”,且要“線程安全”的對象,您會怎么做呢?沒錯,可以使用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,于是錯誤便難免了。這里有一件真人真事,以前某位同學在一堆這樣的代碼中迷茫了,說為什么用了這種方法,還是初始化了多次對象了?檢查了半天沒有看出問題來。最后發現,原因是訪問了錯誤的initialized變量(例如,在某個應該訪問artistInitialized的地方訪問了articleInitialized)。可惜,大段時間已經被浪費了——更糟的是,心情也隨之變差了。

其實,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(上),也很難利用“延遲”所帶來的便利,更難以出現微軟并行擴展、CCR等優秀框架。可以這么說,如果您不善于使用委托,您如果不知道如何合適地使用匿名方法,您在不自知的情況下可能就已經編寫了大量額外的代碼了。

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

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

匿名方法的缺點

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

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

總結

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

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

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

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

相關文章

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

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

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

主站蜘蛛池模板: 国产成人污污网站在线观看 | 精品成人资源在线观看 | 99久久精品免费看国产一区二区 | 免费人成网站永久 | 天天色天天射天天干 | 二区国产 | 色五婷婷| 激情五月开心网 | 国产专区91 | 在线精品国产一区二区 | 在线免费观看色视频 | 黄色大片网站在线观看 | 国产精品永久免费视频观看 | 国产成人精品免费大全 | 婷婷亚洲视频 | 日本又黄又硬又粗好爽视频免费 | 国产一区二区视频在线 | 免费啪视频观在线视频在线 | 亚洲a视频在线 | 色91在线| 狠狠影院 | 国产精品特黄一级国产大片 | 国内精品国语自产拍在线观看91 | 91综合久久久久婷婷 | 色综合国产 | 在线欧美视频 | 亚洲综合色婷婷在线观看 | 国产精品12p| 在线观看色网站 | 青草久久精品 | 国产日韩欧美综合一区二区三区 | 久久国产偷 | 男女一级做a爱过程免费视频 | 欧美成人全部免费观看1314色 | 91精品视频免费在线观看 | 亚洲欧美成人 | 久久精品免费一区二区三区 | 一色屋色费精品视频在线看 | 国产免费久久精品丫丫 | 一本色道久久99一综合 | 国产精品1页 |