各版本.NET委托的寫法回顧
在《關(guān)于最近面試的一點(diǎn)感想》一文中,Michael同學(xué)談到他在面試時詢問對方“delegate在.net framework1.1,2.0,3.5各可以怎么寫”這個問題。于是乎,有朋友回復(fù)道“請問樓主,茴香豆的茴有幾種寫法”,“當(dāng)代孔乙己”,獨(dú)樂,眾樂??戳怂械脑u論,除了某些朋友認(rèn)為“的確不該不知道這個問題”之外,似乎沒有什么人在明確支持樓主。
不過我支持,為什么?因?yàn)槲乙蔡徇^出這樣的問題:各版本.NET委托的寫法有何不同?
這樣,我們暫且不提應(yīng)聘“高級開發(fā)人員”的人,在“自稱熟悉各版本.NET框架”的前提下,是否應(yīng)該知道這個答案。我們也暫且不提Michael同學(xué)提問的“目的”是什么。老趙就先單獨(dú)針對這個問題進(jìn)行解釋,然后談?wù)勛约簽槭裁磿岢鲞@個問題吧。
可能有一件事情需要說在前面,那就是:委托本身其實(shí)從來沒有改變過,改變的一直都是委托的“寫法”。因此更確切地說,改變的只是“編譯器”。而本文所有內(nèi)容都用C#來實(shí)現(xiàn),其實(shí)談得也都是C#編譯器本身——但是其實(shí)VB.NET也有變化埃再由于.NET版本和C#版本的關(guān)系也是非常密切的,因此全文就使用.NET版本進(jìn)行指代了。
.NET 1.x中委托的寫法
委托,如果不追究細(xì)節(jié),從表面上來看我們可以將其通俗地理解為一個安全的“函數(shù)指針”。當(dāng)然,這個函數(shù)指針其實(shí)也是一個對象,有自己的成員,也會封裝了被調(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);
- }
可見,在.NET 1.x中需要使用new DelegateType(...)的方式來創(chuàng)建一個委托對象。不過,作為委托對象內(nèi)部的方法它既可以是實(shí)例方法,也可以是靜態(tài)方法。此外,方法只需要匹配委托類型的簽名和返回值即可,方法參數(shù)的名稱不會成為約束。
嗯,就是這么簡單。
.NET 2.0中委托的寫法
.NET委托引入了范型,且寫法略有簡化:
- public delegate TResult MyFunc
(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ā)人員可以直接將方法賦值給一個委托對象的引用。當(dāng)然,這個改進(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);
- }
匿名方法,簡單地說就是內(nèi)聯(lián)在方法內(nèi)部的委托對象,它的關(guān)鍵便在于形成了一個閉包(委托執(zhí)行時所需的上下文)。如上面的代碼中,BeginGetResponse的***個參數(shù)(委托)可以直接使用TestRequest方法的參數(shù)url,以及方法內(nèi)的“局部”變量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ā)人員需要花費(fèi)大量的精力,為一小部分代碼維護(hù)一大段上下文。例如在這段代碼中,我們會將url和request對象塞入一個object數(shù)組中,在回調(diào)函數(shù)中再通過危險(xiǎn)的Cast操作恢復(fù)數(shù)據(jù)。如果您希望“強(qiáng)類型”,那么只能為每個回調(diào)創(chuàng)建一個新的上下文對象,維護(hù)起來可能更加麻煩——要知道,在并行編程,異步調(diào)用越來越重要的今天,如果沒有匿名方法自動保留上下文的特性,開發(fā)人員會為這些“額外工作”疲于奔命的。
可能您會說,匿名方法的可讀性不佳,因?yàn)樾枰皟?nèi)聯(lián)”。一個方法中內(nèi)聯(lián)太多,維護(hù)成本就上去了,所以匿名方法并不推薦使用。我想說的是,您錯了。如果為了可維護(hù)性,要將方法獨(dú)立拆開,也可以利用匿名方法的優(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表達(dá)式,代碼可以寫的更簡單易讀:
- public static void TestRequest(string url)
- {
- WebRequest request = HttpWebRequest.Create(url);
- request.BeginGetResponse(ar => TestAsyncCallback(ar, request, url), null);
- }
以上就總結(jié)了各版本.NET委托的寫法。
【編輯推薦】