=============================版權申明===============================
任何人都可以轉載,但請注明轉載位址和作者“天轟穿”,因為下面這是我正在寫的 c# 這本書中的節選内容。
整本書都是這個風格,朋友們有啥期待盡管提。
===========================版權申明結束,内容開始======================
6.3.5 匿名方法
小天:我覺得多少還是有點死闆。有不有什麼辦法可以讓委托更靈活,比如某個階段我并不确定具體要調用的被委托方法名。或者根本不想專門為某個委托就寫一個冷僻的方法。
老田:可以使用匿名方法。在 2.0 之前的 C# 版本中,聲明委托的唯一方法是使用命名方法。C# 2.0 引入了匿名方法,而在 C# 3.0 及更高版本中,Lambda 表達式取代了匿名方法,作為編寫内聯代碼的首選方式。不過,本章讨論的這些内容也同樣也适用于 Lambda 表達式(稍後讨論)。有一種情況下,匿名方法提供了 Lambda表達式中所沒有的功能。匿名方法使您能夠省略參數清單,這意味着可以将匿名方法轉換為帶有各種簽名的委托。這對于 Lambda 表達式來說是不可能的。
要将代碼塊傳遞為委托參數,建立匿名方法則是唯一的方法。下面建立一個Windows窗體應用程式,在Form1的設計界面上拖一個button,然後在窗體空白處點右鍵,檢視代碼,在Form1的構造函數中編寫代碼,為button1的click關聯一個匿名方法作為執行體,具體Form1的構造函數代碼如下:
public Form1() { InitializeComponent(); button1.Click += delegate(Object o, EventArgs e) { //匿名事件中的代碼 MessageBox.Show("按鈕事件!"); }; //注意大括号後面有一個分号作為結束 } |
然後運作程式。會發現,點選按鈕執行的事件就是我們上面寫的這個;
小天:匿名方法一定要跟上面示例中這兩個參數嗎?
老田:當然不是,上面示例之是以要跟這兩個參數是因為button的click委托規定了接受他委托的方法的要求是這樣的。否則我們也可以像下面這樣:
//自定義一個委托類型(注意申明委托的位置,錯了别怪我) delegate void Del(int x); //申明一個Del委托類型的執行個體,并為他關聯一個匿名類型的匿名事件 Del d = delegate(int k) { }; |
名方法的優點是減少了要編寫的代碼。不必定義僅由委托使用的方法。在為事件定義委托時,這是非常顯然的。這有助于降低代碼的複雜性,尤其是定義了好幾個事件時,代碼會顯得比較簡單。使用匿名方法時,代碼執行得不太快。編譯器仍定義了一個方法,該方法隻有一個自動指定的名稱,我們不需要知道這個名稱。
在使用匿名方法時,必須遵循兩個規則。在匿名方法中不能使用跳轉語句跳到該匿名方法的外部,反之亦然:匿名方法外部的跳轉語句不能跳到該匿名方法的内部。
在匿名方法内部不能通路不安全的代碼。另外,也不能通路在匿名方法外部使用的ref和out參數。但可以使用在匿名方法外部定義的其他變量。
如果需要用匿名方法多次編寫同一個功能,就不要使用匿名方法。而編寫一個指定的方法比較好,因為該方法隻需編寫一次,以後可通過名稱引用它。
6.3.6 Lambda表達式
C# 3.0為匿名方法提供了一個新的文法:Lambda表達式。Lambda表達式可以用于委托類型。“Lambda表達式”是一個匿名函數,它可以包含表達式和語句,并且可用于建立委托或表達式目錄樹類型。
所有 Lambda 表達式都使用 Lambda 運算符 =>,該運算符讀為“goes to”。該 Lambda 運算符的左邊是輸入參數(如果有),右邊包含表達式或語句塊。Lambda 表達式 x => x * x 讀作“x goes to x * x”。可以将此表達式配置設定給委托類型,如下所示:
class Program { delegate int del(int i); static void Main(string[] args) { del myDelegate = x => x * x; int j = myDelegate(5); //j的值為調用委托所得到的值 Console.WriteLine(j); //值為25 Console.ReadLine(); } } |
現在基本上明白了Lambda表達式的寫法,接下來我們結合匿名方法來做一個執行個體:
class Program { delegate int del(int i); static void Main(string[] args) { del myDelegate = x => x * x; int j = myDelegate(5); //j的值為調用委托所得到的值 Console.WriteLine(j); //值為25 //注意這裡result實際上是del委托類的參數,是以一定是int類型 del d1 = result => { result += 10; //對傳入的result參數做簡單處理 return result; //傳回結果 }; //注意大括号後面有一個分号作為結束 Console.WriteLine(d1(2)); //直接調用委托,結果顯示12 Console.ReadLine(); } } |
老田:給你布置個任務,自定義一個多個參數的委托類型,結合Lambda表達式和匿名方法來做個執行個體。
小天:等等先問兩個問題:
1. 為什麼你上面第一個例題 del myDelegate = x => x * x; 這段代碼中 => 符号後面的x * x 沒有給大括号,但是第二個是給的大括号?
2. 多個參數在 => 符号前面怎麼表示?
老田:第一個問題:如同if語句一樣,如果下面隻有一行代碼,則可以省略大括号;例如下面這樣也是對的
del myDelegate = x =>{return x * x}; |
第二個問題:多個參數任然寫在=> 符号前面,不過需要用括号括起來,同時多個參數之間用逗号分隔,例如
Del d1 = (x, y, z) => z + y + z; |
小天:那就簡單了。如下,我定義了一個名為XiaoTian的委托,兩個參數,然後在匿名方法中對參數進行了簡單的處理,最後傳回這裡我還做了點舉一反三,如下:
class Program { //三個參數的委托 delegate string XiaoTian(string xr, int age, string sex); static void Main(string[] args) { //多個參數就必須使用括号,參數之間用逗号分隔 XiaoTian xt = (xr, age, sex) => { StringBuilder jg = new StringBuilder(); jg.Append("小天此人今年"); jg.Append(age); jg.Append("歲,性别"); jg.Append(sex); jg.Append(",長相"); jg.Append(xr); return jg.ToString(); //直接傳回結果 }; //調用委托 Console.WriteLine(xt("帥得一塌糊塗", 28, "男")); Console.ReadLine(); } } |
運作後效果如圖6-6
圖6-6
老田:不錯。上面我們讨論的都是預定義參數的情況下,那麼假設傳入的參數類型是自定義的類型,恰恰編譯器推斷不出來怎麼辦呢?那麼接下來就要說到的是在為參數指定類型,如下:
//自定義一個有兩個屬性的類 class Thc { public Thc(string n, int a) { Name = n; Age = a; } public string Name { get; set; } public int Age { get; set; } } //控制台應用程式的起始類 class Program { //使用自定義類型的參數 delegate string More(Thc t); static void Main(string[] args) { //對參數申明類型 More m =(Thc t )=> { return "姓名為" + t.Name + ",年齡" + t.Age; }; //調用委托,注意所給的參數是新執行個體一個對象 Console.WriteLine(m(new Thc("天轟穿", 30))); Console.ReadLine(); } } |
小天:上面說了這麼多都是有參數的,如果遇上沒有參數的咋辦?
老田:沒有參數更省事,直接給個空的括号就行,如下:
class Program { //無參數 delegate void Wucanshu(); static void Main(string[] args) { //對于無參數的情況就直接給一個空的括号就行了 Wucanshu wcs = () => { Console.WriteLine( "我就是無參數,你把我咋樣嘛!"); }; //調用委托 wcs(); Console.ReadLine(); } } |