對C# 2.0中匿名方法的種種懷疑分析(轉)

ba發表於2007-08-15
對C# 2.0中匿名方法的種種懷疑分析(轉)[@more@]一、 簡介

所有的方法都使用一個來自於相同集合的元素的子集。在C# 2.0中,可選元素集將會繼續增長。從歷史上看-除了C++內聯方法之外-方法都要求有一個名字、一個返回型別和一個方法體。而且可選擇地,方法可以使用存取修飾符和一個引數列表。在C# 2.0中,方法名已經從必需的變成了可選的。

C# 2.0(一般就代表.net)引入了匿名方法。一個匿名方法可以被用在任何使用代理且該代理被定義為內聯的情況下,它不需要方法名,而具有可選的引數和一個方法體。

為了使用匿名方法,你需要了解什麼是代理。因此,在我們詳細討論何時使用匿名方法以及匿名方法的侷限性之前,先讓我們簡要地回顧一下代理。

二、 代理回顧

匿名方法對於宣告和使用代理來說是一種壓縮方式(如果你對什麼是代理還有疑問,請繼續閱讀;否則,可以跳過下面的這一部分)。代理,作為一種指向函式簽名的指標,在.NET語言之前的語言中就已存在。切記,在計算機中一切其實都是位和位元組。透過引入函式指標技術,有可能動態地把一些未來的目前尚未知的函式賦給指標,並由此誕生了事件。

函式指標的基本使用方法是,可以把一個函式的地址賦給一個單一的指標。為了透過一個指標來呼叫該函式,程式設計師要對之進行檢查以決定是否這個指標為null,然後間接地透過這個指標呼叫這個函式。總之,要使用指標,必須進行null檢查,而現在"一個指標對應一個函式"作為一種限制也該到結束的時候了。

回顧一下來分析,代理會成為原始函式指標的下一個進化替代者。一個代理即是一個類,它對該指標進行了封裝;隱含地,.NET中的代理是multicast代理。作為一個multicast代理僅僅意味著不再存在"一個函式對應一個指標"的限制,因為multicast代理類包含一個指標列表。包含一個內部列表意味著多於一個函式的地址可以被賦值給一個單一的代理。當該代理-你可以認為是"事件"-被激發或呼叫時,所有的內部列表函式被呼叫。

注意 在C#中,我們呼叫代理的方式就象從前我們呼叫方法以及呼叫所有的賦值函式一樣;但是我們仍然能夠進行null檢查。在Visual Basic.NET中,null檢查隱含在啟用事件行為中。

在C#中,函式地址透過使用一個過載的+=運算子插入到一個列表中並且經由一個過載的-=運算子而被刪除。C#還支援手工地定義新增和刪除塊;新增和刪除對於代理恰似get和set對於屬性。

在C# 1.0和C# 1.1中,典型情況下,我們把代理例項賦給事件屬性。例如,在WinForms中,一個Button控制元件暴露一個Click事件。Click的代理型別是 EventHandler。EventHandler是一個以物件和EventArgs為引數的方法。因此,我們可以用匹配代理EventHandler 的簽名的任何方法來初始化一個EventHandler物件並且把代理賦給Click。下面是該程式碼看上去的樣子:

private void Form1_Load(object sender, EventArgs e)

{ button1.Click += new EventHandler(OnClick);}

private void OnClick(object sender, EventArgs e)

{ Debug.WriteLine("button1 clicked");}

因為WinForms的表單設計器和WebForms的頁面設計器自動地新增代理繫結;所以,我們有可能不需要手工式地繫結代理而建立大量的程式碼。

三、 匿名方法是內聯代理

通常,當我們使用代理時,我們總是有一個方法。該方法的簽名匹配代理的簽名規定並且能被用來初始化一個代理例項。匿名方法用於把方法和代理的初始化壓縮到一個單一的位置。

透過使用前一節的例子,我們已看到代理new EventHandler的例項化是怎樣區別於用來初始化該代理的方法OnClick的。這部分程式碼能被壓縮成一個匿名方法:

private void Form1_Load(object sender, EventArgs e){

 button1.Click += delegate

 {

  Debug.WriteLine("button1 clicked");

 };

}

為了建立該匿名方法,請注意我們刪除了OnClick的方法頭並且用OnClick 的方法體的單詞delegate代替了EventHandler代理的構造器。其所導致的結果行為是相同的。如果我們想使用事件引數,我們通常與代理相關聯,我們可以在單詞delegate之後新增一可選的引數列表:

private void Form1_Load(object sender, EventArgs e){

button1.Click += delegate(object s, EventArgs ev)

{ Debug.WriteLine("object is " + s.ToString()); };

}

如果你定義代理引數,它們必須匹配代理型別所定義的引數。例如,Click的型別是EventHandler,因此如果引數存在,它們必須匹配EventHandler的引數物件和EventArgs。

匿名方法可以被使用在任何需要使用代理的地方。匿名方法可以使用ref和out引數,但是不能使用全域性範圍的reference ref或out引數。匿名方法不能使用unsafe編碼,並且匿名方法不能以使得分支行為跳出匿名方法的程式碼塊的方式來使用goto,break或 continue等語句。

四、 市場調查結果

匿名方法是好東西嗎?市場調查證明匿名方法確實不錯,因為它們能夠減少由於例項化代理和減少分離方法所導致的程式碼開銷。而且市場調查還證明匿名方法增強了可用性和可維護性。我認為良好命名的方法也可以實現這一點。請看下面的程式碼容易維護嗎?

private void Form1_Load(object sender, EventArgs e)

{

 BindClick(delegate { Debug.WriteLine("button1 click"); });

}

private void BindClick(EventHandler handler)

{

 button1.Click += handler;

}

在這個例子中,我們把一個代理傳遞給一個方法-透過把該代理作為一個匿名方法傳遞。僅是保持圓括號、分號和方括號的順序和個數就已令人十分頭疼。

如果引用經典示例來說明,那就是匿名方法僅僅是因剔除了執行緒(它們使用代理)而減少了相應的建立代理和方法的開銷。這倒是真的,但是執行緒並不經常使用並且想正確使用也非常困難。我在想,要想使程式碼更為秘密些而不是更為公開些該是多麼謹慎的一件事情。

就語言方面來講,我喜歡方法;但是作為一個實際開發中的事物,匿名方法也許僅是微軟的某個發明者有點太聰明的一種證明。

五、 總結

匿名方法是可以存在沒有名字的方法的證明-它們可以被定義並使用在任何能夠使用代理的地方。代理是事件處理器的包裝器。匿名方法到底有多大的實用性和普遍使用價值還有待於進一步的實踐證明。我懷疑,匿名方法將不會比運算子過載有更大的用途,並且其使用也會少之又少;但是匿名方法現在已是.NET的一部分,所以在閱讀程式碼時能夠識別出它們來還是很有必要的。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10617731/viewspace-957871/,如需轉載,請註明出處,否則將追究法律責任。

相關文章