C#進階之全面解析Lambda表達式

引言

在實際的項目中遇到一個問題,咱們常常在網上搜索複製粘貼,其中有些代碼看着很是的簡潔,好比Lambda表達式,可是一直沒有去深刻了解它的由來,以及具體的使用方法,因此在使用的時候比較模糊,其次,編程涉及面比較廣,咱們不可能每一個方面都去精通了解,但常常運到的一些東西,必須瞭解其具體使用方法及使用場景,才能書寫出優美、簡潔、可讀性強的代碼。筆者經過搜索、整理資料及測試代碼,詳細的介紹Lambda 表達式的用法。程序員

Lambda 表達式概念

「Lambda 表達式」(lambda expression)是一個匿名函數,能夠表示爲委託的代碼,或者表示爲表達式樹的代碼,它所表示的表達式樹能夠編譯爲委託。 Lambda 表達式的特定委託類型取決於其參數和返回值。不返回值的 Lambda 表達式對應於 Action 委託,具體取決於其參數數量。 返回值的 Lambda 表達式對應於 Func 委託,具體取決於其參數數量。express

Lambda 表達式普遍用於:編程

  • 將要執行的代碼傳遞給異步方法,例如 Task.Run(Action)。api

  • 編寫 LINQ 查詢表達式。異步

  • 建立表達式樹。async

C# 中委託的演變

在 C# 1.0 中,經過使用在代碼中其餘位置定義的方法顯式初始化委託來建立委託的實例。 C# 2.0 引入了匿名方法的概念,做爲一種編寫可在委託調用中執行的未命名內聯語句塊的方式。 C# 3.0 引入了 Lambda 表達式,這種表達式與匿名方法的概念相似,但更具表現力而且更簡練。 這兩個功能統稱爲匿名函數。 一般,面向 .NET Framework 3.5 及更高版本的應用程序應使用 lambda 表達式。函數

C#1.0中委託的實現,代碼以下:學習

 delegate int CalculateHandler(int x, int y);
 private int Sum(int x, int y)
 {
    return x + y;
 }
 public void Test()
 {
    CalculateHandler sumHandler =new CalculateHandler(Sum);
    MessageBox.Show(sumHandler(1, 2).ToString());//輸入結果3
}

C#2.0中匿名方法的實現,代碼以下:測試

delegate int CalculateHandler(int x, int y);
public void Test()
{
    CalculateHandler sumHandler = delegate(int x, int y) { return x + y; };
    MessageBox.Show(sumHandler(1, 2).ToString());//輸入結果3
}

C#3.0中Lambda 表達式的實現,代碼以下:spa

 delegate int CalculateHandler(int x, int y);
 public void Test()
 {
      CalculateHandler sumHandler = (x, y) => x + y;
      MessageBox.Show(sumHandler(1, 2).ToString());//輸入結果3
 }

由此能夠看出微軟的一步步升級,帶給咱們的是編程上的優美,簡潔,可讀性強,所以做爲程序員咱們要一直處於學習的路上。

Lambda 表達式使用

C#的Lambda 表達式都使用 Lambda 運算符 =>,該運算符讀爲「goes to」, 若要建立 Lambda 表達式,須要在 lambda 運算符左側指定輸入參數(若是有),而後在另外一側輸入表達式或語句塊。 例如,單行 Lambda 表達式 x => x * x 指定名爲 x 的參數並返回 x 的平方值。

在介紹Lambda 表達式使用以前咱們先了解.Net爲咱們定義好的Action<T>和Func<T>兩個泛型委託。

Action<T>泛型委託

Action<T>委託表示引用一個返回類型爲Void的方法。這個委託存在不一樣的變體,能夠傳遞之多16個不一樣的參數類型。同時,沒有泛型參數的Action類能夠調用沒有參數的方法。例如,Action<in T>表示有一個輸入參數的方法,Action<in T1,in T2>表示有兩個輸入參數的方法。

Func<T>泛型委託

Func<T>能夠以相似的方法使用。不過Func<T>容許調用帶返回參數的方法。Func<T>也有不一樣的變體,之多能夠傳遞16個參數和一個返回類型。例如:Func<out TResult>委託類型能夠無參的帶返回類型的方法,Func<in T1,inT2,out Tresult>表示帶兩個參數和一個返回類型的方法。

Func<T>能夠表示帶輸出的方法,T能夠有多個,且只有最後一個表示輸出即最後一個是返回類型。Func<in T1,inT2,out Tresult>中的字符in、out在實際代碼中是不會出現的。

表達式 Lambda

表達式位於 => 運算符右側的 Lambda 表達式稱爲「表達式 lambda」。具體形式:(input-parameters) => expression,表達式 lambda 會返回表達式的結果。

具體事例,代碼以下:

 public Action SuccessPrompt =() => MessageBox.Show("執行成功!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information); //沒有參數,括號不能省略
 public Func<int, int, bool> Compare = (x, y) => x > y;//判斷x是否大於y

語句 Lambda

語句 Lambda 與表達式 lambda 表達式相似,只是語句括在大括號中,具體形式:(input-parameters) => { statement; }。語句 lambda 的主體能夠包含任意數量的語句;可是,實際上一般不會多於兩個或三個。

具體事例代碼以下:

 public Action<string> Prompt = prompt =>
        {
            MessageBox.Show(prompt, "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
        };//僅當 Lambda 只有一個輸入參數時,括號纔是可選的;不然括號是必需的

異步 Lambda

經過使用 async 和 await 關鍵字,你能夠輕鬆建立包含異步處理的 lambda 表達式和語句。其中async 和 await 關鍵字是在 C# 5 中引入的。

await關鍵字

await 運算符應用於異步方法中的任務,在方法的執行中插入掛起點,直到所等待的任務完成。 任務表示正在進行的工做。

await 僅可用於由 async 關鍵字修改的異步方法中。 使用 async 修飾符定義而且一般包含一個或多個 await 表達式的這類方法稱爲異步方法。

async修飾符

使用 async 修飾符可將方法、lambda 表達式或匿名方法指定爲異步。 若是對方法或表達式使用此修飾符,則其稱爲異步方法。

使用異步 lambda 添加事件處理程序。 若要添加此處理程序,請在 lambda 參數列表前添加 async 修飾符,代碼以下:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        button1.Click += async (sender, e) =>
        {
            await ExampleMethodAsync();
            textBox1.Text += "\r\nControl returned to Click event handler.\n";
        };
    }

    private async Task ExampleMethodAsync()
    {
        // The following line simulates a task-returning asynchronous process.
        await Task.Delay(1000);//Task.Delay方法只會延緩異步方法中後續部分執行時間,當程序執行到await表達時,一方面會當即返回調用方法,執行調用方法中的剩餘部分,這一部分程序的執行不會延長。另外一方面根據Delay()方法中的參數,延時對異步方法中後續部分的執行。
    }
}

Lambda 表達式和元組

自 C# 7.0(對應 .NET Framework4.7和Visual Studio 2017 )起,C# 語言提供對元組的內置支持。 能夠提供一個元組做爲 Lambda 表達式的參數,同時 Lambda 表達式也能夠返回元組。 在某些狀況下,C# 編譯器使用類型推理來肯定元組組件的類型。可經過用括號括住用逗號分隔的組件列表來定義元組,一般,元組字段命名爲 Item1Item2 等等。可是,可使用命名組件定義元組。

事例代碼以下:

public void Test1()
{
    Func<(int, int, int), (int, int, int)> doubleItem = ns => (2 * ns.Item1, 2 * ns.Item2, 2 * ns.Item3);
    var itemList = (1, 2, 3);
    var resultDItemList = (itemList);//結果爲[1, 2, 9]
}

public void Test2()
{
    Func<(int x, int y, int z), (int, int, int)> doubleItem = ns => (2 * ns.x, 2 * ns.y, 2 * ns.z);
    var itemList = (1, 2, 3);
    var resultDItemList = (itemList);//結果爲[1, 2, 9]
}

含標準查詢運算符的 Lambda

在其餘實現中,LINQ to Objects 有一個輸入參數,其類型是泛型委託 Func<TResult> 系列中的一種。 這些委託使用類型參數來定義輸入參數的數量和類型,以及委託的返回類型。 Func 委託對於封裝用戶定義的表達式很是有用,這些表達式將應用於一組源數據中的每一個元素。

標準查詢運算符是組成 LINQ 模式的方法。 這些方法中的大多數都做用於序列;其中序列指其類型實現 IEnumerable<T> 接口或 IQueryable<T> 接口的對象。 標準查詢運算符提供包括篩選、投影、聚合、排序等在內的查詢功能。

共有兩組 LINQ 標準查詢運算符,一組做用於類型 IEnumerable<T> 的對象,另外一組做用於類型 IQueryable<T> 的對象。 構成每一個集合的方法分別是 Enumerable 和 Queryable 類的靜態成員。 這些方法被定義爲做爲方法運行目標的類型的擴展方法。 這意味着可使用靜態方法語法或實例方法語法來調用它們。

具體事例代碼以下:

        public class Sutdent
        {
            public string Id { get; set; }
            public string Name { get; set; }
            public int Age { get; set; }
        }
        static void Main(string[] args)
        {
            List<Sutdent> studentList = new List<Sutdent>();
            studentList.Add(new Sutdent {Id = "001", Name = "張三", Age = 18});
            studentList.Add(new Sutdent {Id = "002", Name = "李四", Age = 19});
            studentList.Add(new Sutdent {Id = "003", Name = "王五", Age = 16});
            studentList.Add(new Sutdent {Id = "004", Name = "趙六", Age = 17});
            
            List<Sutdent> list1 = studentList.FindAll(st => st.Age > 17);//選擇年齡大於17的全部學生
            List<Sutdent> list2 = studentList.Where(st => st.Age > 17).ToList();//選擇年齡大於17的全部學生
            studentList.Sort((st1,st2)=>st2.Age-st1.Age);//按Age降序排列
            List<string> list3 = studentList.Select(st => st.Name).ToList();//選擇列表中的全部名字
           
        }

Lambda 表達式中的類型推理

編寫 Lambda 時,一般沒必要爲輸入參數指定類型,由於編譯器能夠根據 Lambda 主體、參數類型以及 C# 語言規範中描述的其餘因素來推斷類型。

lambda 類型推理的通常規則以下:

  • Lambda 包含的參數數量必須與委託類型包含的參數數量相同。

  • Lambda 中的每一個輸入參數必須都可以隱式轉換爲其對應的委託參數。

  • Lambda 的返回值(若是有)必須可以隱式轉換爲委託的返回類型。

總結

經過上邊的講解,咱們能夠看出Lambda表達式的用法很是的簡單,特別在標準查詢運算符中應用很是普遍,提升了編程效率,且寫出的代碼很是的簡潔。文中如有不足之處,還望海涵,博文寫做不易但願多多支持,後續會更新更多內容,感興趣的朋友能夠加關注,歡迎留言交流! 

相關文章
相關標籤/搜索