在C# 1.1裡,聲明和使用委托要求你有委托和一個在委托被觸發時具有匹配簽名的能夠執行的方法,以及一個將命名方法與委托關聯的分配語句。作為C# 2.0的新特性,匿名方法基本上能夠提供與先前命名方法相同的功能,但是它已經不再需要一個在關聯到委托之前就明確創建的方法了。
你可以把匿名方法想象為一個實現與委托進行關聯這項功能的便捷途徑。如果同時看一下匿名方法實現和命名方法實現所取得IL結果,你會發現這兩者之間的差別非常小。當編譯器碰到匿名方法的時候,它會在類裡面創建一個命名方法,並將它與委托進行關聯。所以匿名方法在運行期間與命名方法的性能非常類似——性能的增加體現在開發人員的生產效率上,而不是運行期間的執行上。
如何使用匿名方法
多用代碼少用說教來解釋和理解匿名方法會容易一些。下面的例子應該能夠說明你自己可以如何利用匿名方法的優勢:
例1——基礎知識
使用匿名方法很簡單。你只需要把匿名方法放在你通常放命名方法的關聯語句裡。在下面這個例子裡,我把匿名方法和示例1的委托關聯在一起:
示例列表1
#region Simple example - Example1
private delegate void Example1();
privatevoid btnExample1_Click(object sender, EventArgs e)
{
//Declare an instance of the Example1 delegate.
// You can see where I'm using the anonymous
// method in place of a named method - it follows
// the delegate() keyword.
Example1 example =
newExample1(
delegate()
{
MessageBox.Show("Example1");
});
example();
}
#endregion
例2——變量范圍
任何在匿名方法裡聲明的變量的范圍都不會超出匿名方法的代碼塊。但是,匿名方法確實具有訪問它們代碼塊之外的變量的能力,只要這些變量在匿名方法所使用的范圍裡。這些變量被微軟稱為外部變量。下面示例2顯示了匿名方法如何引用外部變量:
示例列表2
#region Variable scope example - Example2
private delegate void Example2();
privatevoid btnExample2_Click(object sender, EventArgs e)
{
//Setup our parameters.
string firstName = "Zach";
string lastName = "Smith";
//Create an instance of the Example2 delegate with an
// anonymous method.
Example2 example =
newExample2(
delegate() {
MessageBox.Show(firstName " " lastName);
});
//Execute the delegate.
example();
}
#endregion
要注意的是,根據MSDN的文檔,匿名方法裡的ref和out參數無法被訪問到。
例3——參數的傳遞
你可以將參數傳遞給匿名方法,方式就和你處理引用命名方法參數的委托一樣。下面的示例3說明這種類型的功能:
示例列表3
#region Parameter example - Example3
private delegate void Example3(string firstName, string lastName);
privatevoid btnExample3_Click(object sender, EventArgs e)
{
//Setup our parameters.
string parameter1 = "Zach";
string parameter2 = "Smith";
//Create an instance of the Example3 delegate with an
// anonymous method.
Example3 example =
newExample3(
delegate(string firstName, string lastName)
{
MessageBox.Show("Example3: " firstName " " lastName);
});
//Execute the delegate.
example(parameter1, parameter2);
}
#endregion
例4——多個方法的關聯
就和命名方法一樣,將多個匿名方法與同一個委托進行關聯是可能的。這在很多情況下會非常有用——首先想到的是把一個簡單的處理程序添加給按鈕的點擊事件。下面的代碼(示例4)顯示了一個委托,它同時帶有與之相關聯一個匿名方法和一個命名方法:
示例列表4
#region Multiple method association (stacking) - Example4
private delegate void Example4(string firstName, string lastName);
privatevoid btnExample4_Click(object sender, EventArgs e)
{
//Setup our parameters.
string parameter1 = "Zach";
string parameter2 = "Smith";
//Create an instance of the Example4 delegate with an
// anonymous method.
Example4 example =
newExample4(
delegate(string firstName, string lastName)
{
MessageBox.Show("Example4: " firstName " " lastName);
});
//Add another method to the delegate - this time
// a named method.
example = newExample4(Example4NamedMethod);
//Execute the delegate.
example(parameter1, parameter2);
}
privatevoid Example4NamedMethod(string firstName, string lastName)
{
MessageBox.Show("Example4Method: " firstName " " lastName);
}
#endregion
例5——將匿名方法作為參數傳遞
就和命名方法一樣,將匿名方法作為參數傳遞給函數是可能的。這並不是一個我認為會通常使用的特性,但是我敢肯定未來會有這種需要。下面的代碼(例5)說明了這種類型的功能,它將一個命名方法作為參數傳遞給了函數:
示例列表5
#region Passing anonymous methods - Example5
private delegate void Example5(string firstName, string lastName);
privatevoid btnExample5_Click(object sender, EventArgs e)
{
//Execute Passit and pass the anonymous method.
Passit((Example5)delegate(string firstName, string lastName)
{
MessageBox.Show("Example5: " firstName " " lastName);
});
//Execute Passit with the named method.
Passit(Example5NamedMethod);
}
privatevoid Example5NamedMethod(string firstName, string lastName)
{
MessageBox.Show("Example5Method: " firstName " " lastName);
}
privatevoid Passit(Example5 example)
{
example("Zach", "Smith");
}
#endregion
例6—-訪問類成員
這是對上面例2的變量范圍的擴展。但是,這個例子(例6)說明了匿名參數還能夠在它們的代碼塊之外執行命名方法:
示例列表6
#region Accessing class members - Example6
private delegate void Example6();
privateint _customerId;
privatestring _customerCode;
publicint CustomerID
{
get { return _customerId; }
set { _customerId = value; }
}
publicstring CustomerCode
{
get { return _customerCode; }
set { _customerCode = value; }
}
privatevoid btnExample6_Click(object sender, EventArgs e)
{
//Populate out properties.
this.CustomerID = 90;
this.CustomerCode = "1337HK";
//Setup the delegate/anonymous method.
Example6 example =
newExample6(
delegate
{
this.ShowCustomer(this.CustomerID, this.CustomerCode);
});
//Execute the delegate.
example();
//Change the properties.
this.CustomerID = 54;
this.CustomerCode = "L4M3";
//Execute the delegate again.
// Notice that the new values are reflected.
example();
}
privatevoid ShowCustomer(int customerId, string customerCode)
{
MessageBox.Show(
String.Format("CustomerID: {0}nCustomer Code: {1}",
customerId, customerCode));
}
#endregion
要注意的是,我兩次調用了與匿名方法相關聯的委托。你可能會發現一個很有趣的事情:在這些調用中,方法會輸出兩組不同的值。這是因為用在匿名方法裡的外部變量在創建匿名方法的時候被引用。這意味著對這些變量的任何更改都會在匿名函數訪問變量的時候被反映出來。
你可能還注意到在這個實例裡委托關鍵字後面沒有括號。當匿名方法不需要帶參數的時候,後面的括號是可選的。
評論
我希望本文已經說明如何使用匿名方法。雖然它們還不是革命性的,但是它們是C#演化成為一門程序員友好的語言過程中的一個重要步驟。