[.net 面向對象程序設計進階] (5) Lamda表達式(一) 創建委托


[.net 面向對象程序設計進階] (5) Lamda表達式(一)  創建委托 

本節導讀: 

通過學習Lambda表達式,學會創建委托和表達式目錄樹,深入了解Lambda的特性,讓你的代碼變的更加清晰、簡潔、高效。 

讀前必備: 

本節學習前,需要掌握以下知識: 

A.泛型        (請參考[.net 面向對象編程基礎]  (18) 泛型) 

B.Linq基礎 (請參照[.net 面向對象編程基礎] (19) LINQ基礎) 

C.Linq使用  (請參照[.net 面向對象編程基礎 (20) LINQ使用

D.委托       (請參照[.net 面向對象編程基礎]   (21) 委托) 

E.事件      (請參照[.net 面向對象編程基礎]   (22) 事件) 

通過《.net 面向對象編程基礎》系列中相關介紹,我們已經初步使用過了Lambda表達式進行Linq查詢,這節我們主要深入了解Lambda表達式。

1. 關於Lambda 

Lambda 表達式是一種可用於創建委托或表達式目錄樹類型的匿名函數。 

以上是微軟對Lambda表達式的定義。從這個定義中,我們可以看出,Lambda的存在,主要做兩件事: 

A.創建委托(Delegate) 

B.創建表達式樹(Expression Tree) 

此外,Lambda表達式的本質就是匿名方法(或叫匿名函數)。 

后面面我們分別從這兩個方法入手,進一步學習Lambda帶我給我們的便利。 

2. Lambda表達式 

Lambda表達式的構成如下: 

零個參數: ()=>expr 

一個參數:(param)=>expr 或 param=>expr 

多個參數:(param-list)=>expr 

所有Lambda表達式都使用Lambda運算符=>,該運算符讀作"goes to" 

當參數只有一個時,右邊的括號可以省略。 

下面是寫法舉例:

//零個參數
() => MethodName()
//一個參數
(x) => x+x
X=>x+x
//兩個及以上參數
(m,n) => m.Length>n
//顯式類型參數
(int x,string y)=>y.Length>x

上面的示例中,其中顯式類型參數,是當編譯器無法推斷其參數類型時,可以顯式的定義參數類型。

 3. Lambda語句

Lambda語句和Lambda表達式類似,只是右邊部分寫在{}

Lambda語句構成如下:

(input parameters) => {statement;}

示例:

delegate void TestDelegate(string s);
…
TestDelegate myDel = n => { string s = n + " " + "World"; Console.WriteLine(s); };
myDel("Hello");

4.異步Lambda(async和await)

我們通過在《.net 面向對象編程基礎》中學習,了解了事件本身也是一種特殊的委托,那么Lambda可用於創建委托也就是說同樣可以來創建事件。

下面看示例:

首先看一個普通按鈕點擊事件的實現及Lambda語句寫法

下面是普通寫法:

 this.myButton.Click += new System.EventHandler(this.myButton_Click);

//事件通常委托寫法
private void myButton_Click(object sender, EventArgs e)
{
    MessageBox.Show("您好,我是按鈕點擊事件!");
}

下面是Lambda寫法:

//事件Lambda語句寫法
myButton.Click += (sender, e) => { MessageBox.Show("您好,我是按鈕點擊事件!這是Lambda語句寫法"); };

上面兩種寫法,我們在前面的章節中已經有很多例子了。

下面我們看一下異步的事件:

//異步事件普通寫法
private async void asyncButton_Click(object sender, EventArgs e)
{
    await MyMethodAsync();
    MessageBox.Show("您好,我是按鈕點擊事件!異步的喲!");

}
async Task MyMethodAsync()
{            
    await Task.Delay(1000);
}

我們用Lambda來改寫上面的異步事件:

//異步事件Lambda寫法
asyncButton.Click += async (sender, e) => { await MyMethodAsync();  MessageBox.Show("您好,我是按鈕點擊事件!異步的喲!這是Lambda語句寫法"); };
async Task MyMethodAsync()
{            
    await Task.Delay(1000);
}

上面的異步事件,就是在事件委托階段使用async來表示這是一個異步的,在事件處理中階段使用await關鍵詞來指定一個異步方法。

Lambda語句寫法同樣是針對異步事件的簡潔寫法,具有相同的效力。

5. 標准查詢運算的Lambda表達式

5.1泛型委托 使用Lambda

我們在[.net 面向對象編程基礎]   (21) 委托一節中說到了三種常用的泛型委托

Action(無返回值泛型委托)

Func(有返回值泛型委托)

predicate(返回值為bool型的泛型委托)

這節不再重復說明泛型委托,不熟悉泛型委托的小伙伴,請參考,我們只舉例說明泛型委托和它的Lambda寫法

//Action 無返回值類型的 泛型委托
//匿名方法聲明及調用
Action<int, int> act = delegate (int a, int b) {
    Console.WriteLine(a + "+" + b + "=" + (a + b));
};
act(11, 22);
//表達式聲明及調用
Action<int, int> actLambda = (a, b) => { Console.WriteLine(a + "+" + b + "=" + (a + b)); };
actLambda(111, 222);

//Func 帶返回值的 泛型委托
//匿名方法聲明及調用
Func<int, int, string> acc = delegate (int a, int b) {
    return (a + "+" + b + "=" + (a + b));
};
Console.WriteLine(acc(11, 22));
//表達式聲明及調用
Func<int, int, string> ac = (a, b) => { return (a + "+" + b + "=" + (a + b)); };
Console.WriteLine(ac(111, 222));

5.2 Linq中使用Lambda

關於在Linq中使用Lambda表達式,我們在[.net 面向對象編程基礎 (20) LINQ使用有詳細說明了,不熟悉的小伙伴請參考,下面我們舉例說明幾種常用的Lambda查詢寫法。

5.2.1 Count 求數量

//查詢下列數中的奇數 - Lambda寫法
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
int oddNumbers = numbers.Count(n => n % 2 == 1);
Console.WriteLine("奇數有:"+oddNumbers.ToString()+ "個,Lambda寫法");
//查詢下列數中的奇數 -Linq查詢寫法
var oddNumbersFunc = (from num in numbers
                        where num % 2 == 1
                        select num
                    ).Count();

Console.WriteLine("奇數有:" + oddNumbersFunc.ToString() + "個,Linq查詢寫法");

運行結果如下:

5.2.2  TakeWhile 滿足條件就返回集合

//TakeWhile只要滿足指定的條件就返回,在檢檢到number中的6時,就返回,其中8 和9 不滿足條件,則返回 5 4 1 3 
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
var firstNumbersLessThan6 = numbers.TakeWhile(n => n < 6);
Console.WriteLine("滿足條件就返回的數有:" + firstNumbersLessThan6.Count().ToString() + "個,Lambda寫法");
//第二個參數index表示n的索引位置
string NewNumber = String.Empty;
numbers.TakeWhile((n, index) => n >= index).ToList().ForEach(m=> NewNumber+=m +" ");
Console.WriteLine("滿足條件就返回的數有:" + NewNumber + ",Lambda寫法");

 運行結果如下:

6. Lambda的類型推理

在編寫 lambda 時,通常不必為輸入參數指定類型,因為編譯器可以根據 lambda 主體、參數的委托類型以及 C# 語言規范中描述的其他因素來推斷類型。 對於大多數標准查詢運算符,第一個輸入是源序列中的元素類型。 因此,如果要查詢 IEnumerable<Customer>,則輸入變量將被推斷為 Customer 對象,這意味着你可以訪問其方法和屬性:

//Lambda的類型推理
List<MyClass> list = new List<MyClass>() {
    new MyClass(){  at1 = "aaa", at2 = 2, at3 = DateTime.Now},
    new MyClass{ at1 = "bbb", at2 = 5, at3 = DateTime.Parse("2015-06-07") },
    new MyClass{ at1 = "aaa", at2 = 1, at3 = DateTime.Parse("2010-11-12")  }
};            
Console.WriteLine("at1為aaa的元素有:" + list.Where(m => m.at1 == "aaa").Count() + "個,Lambda寫法"); 
class MyClass {
    public  string at1 { get; set; }
    public int at2 { get; set; }
    public DateTime at3 { get; set; }
}

運行結果如下:

Lambda 的一般規則如下:

A. Lambda 包含的參數數量必須與委托類型包含的參數數量相同。

B. Lambda 中的每個輸入參數必須都能夠隱式轉換為其對應的委托參數。

C. Lambda 的返回值(如果有)必須能夠隱式轉換為委托的返回類型。

請注意,lambda 表達式本身沒有類型,因為常規類型系統沒有“Lambda 表達式”這一內部概念。但是,有時以一種非正式的方式談論 lambda 表達式的“類型”會很方便。 在這些情況下,類型是指委托類型或 lambda 表達式所轉換到的 Expression 類型。

7. Lambda表達式的變量范圍

我們在[.net 面向對象編程基礎]  (19) LINQ基礎 一節中說到匿名方法時提到匿名方法可以引用滿園內的外部變量,前面示例中也有提及。

下面我們看一個示例: 

class Program
{
    static int num = 2;
    static void Main(string[] args)
    {
        int num2 = 7;

        //Lambda表達式的變量范圍
        //相對以下表達式內部,一個類的字段num和一個變量num2,下面測試在表達式內部調用
        int[] numbers2 = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
        //我們對大於num小於num2的數求和
        int sum = numbers2.Where(m => m > num && m < num2).Sum();
        Console.WriteLine("大於num小於num2的和為:" + sum);
        Console.ReadKey();
    }
}

下列規則適用於 lambda 表達式中的變量范圍:

A.捕獲的變量將不會被作為垃圾回收,直至引用變量的委托符合垃圾回收的條件。

B.在外部方法中看不到 lambda 表達式內引入的變量。

C.Lambda 表達式無法從封閉方法中直接捕獲 ref 或 out 參數。

D.Lambda 表達式中的返回語句不會導致封閉方法返回。

E.如果跳轉語句的目標在塊外部,則 lambda 表達式不能包含位於 lambda 函數內部的 goto 語句、break 語句或 continue 語句。 同樣,如果目標在塊內部,則在 lambda 函數塊外部使用跳轉語句也是錯誤的。

8.要點:

本節主要說明了:

Lambda表達式在創建委托中的應用;

Lambda表達式在Linq查詢中的應用;

Lambda 語句在異步事件中的應用;

Lambda 的兩個特性:類型推理和外部變量引用

下一節,我們主要說明Lambda的另一個特點,就是創建表達式目錄樹(Expression Tree)。

==============================================================================================  

 返回目錄

 <如果對你有幫助,記得點一下推薦哦,如有

有不明白或錯誤之處,請多交流>  

<對本系列文章閱讀有困難的朋友,請先看《.net 面向對象編程基礎》>

<轉載聲明:技術需要共享精神,歡迎轉載本博客中的文章,但請注明版權及URL>

.NET 技術交流群:467189533    .NET 程序設計

==============================================================================================   


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM