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

来源:https://www.cnblogs.com/aizai846/archive/2019/06/21/11059247.html
-Advertisement-
Play Games

微軟從C#1.0的委托,到C#2.0的匿名方法,再到C#3.0的Lambda表達式,一步步升級,帶給我們的是代碼上的優美,簡潔,可讀性強。Lambda 表達式廣泛用於編寫LINQ查詢表達式等方面。本文全面的介紹了Lambda 表達式的概念、由來及使用方法。 ...


引言

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

Lambda 表達式概念

“Lambda 表達式”(lambda expression)是一個匿名函數,可以表示為委托的代碼,或者表示為表達式樹的代碼,它所表示的表達式樹可以編譯為委托。 Lambda 表達式的特定委托類型取決於其參數和返回值。不返回值的 Lambda 表達式對應於 Action 委托,具體取決於其參數數量。 返回值的 Lambda 表達式對應於 Func 委托,具體取決於其參數數量。

Lambda 表達式廣泛用於:

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

  • 編寫 LINQ 查詢表達式。

  • 創建表達式樹。

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 表達式的實現,代碼如下:

 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表達式的用法非常的簡單,特別在標準查詢運算符中應用非常廣泛,提高了編程效率,且寫出的代碼非常的簡潔。文中若有不足之處,還望海涵,博文寫作不易希望多多支持,後續會更新更多內容,感興趣的朋友可以加關註,歡迎留言交流! 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ``` Date: 表示特定的瞬間,精確到毫秒,通過方法設定自己所表示的時間,可以表示任意的時間 System.currentTimeMillis() :返回的當前系統時間, 1970-1-1 至今的毫秒數 SimpleDateFormat sdf = new SimpleDateFormat("y... ...
  • 1.遍歷key值 同理,可把下列代碼的d改為d.keys() notice:python2除了上述兩種方法外,還可以寫為d.iterkeys() 2.遍歷value值 同理,可把下列代碼的d改為d.values() notice:python2除了上述兩種方法外,還可以寫為d.itervalues( ...
  • 容器可設置佈局管理器,管理容器中組件的佈局: container.setLayout(new XxxLayout()); Java有6種佈局管理器,AWT提供了5種: FlowLayout BorderLayout GridLayout GridBagLayout CradLayout Swing還 ...
  • Github https://github.com/gongluck/SDL2 study/tree/master/Csdl2 Csdl2.h Csdl2.cpp 測試 C++ include "Csdl2.h" include include include define TESTCHECKRET ...
  • 讓我們考慮一個簡單的編程挑戰:對大數組中的所有元素求和。現在可以通過使用並行性來輕鬆優化這一點,特別是對於具有數千或數百萬個元素的巨大陣列,還有理由認為,並行處理時間應該與常規時間除以CPU核心數一樣多。事實證明,這一壯舉並不容易實現。我將向您展示幾種並行執行此操作的方法,它們如何改善或降低性能以及 ...
  • 在前面兩篇隨筆《ABP開發框架前後端開發系列---(7)系統審計日誌和登錄日誌的管理》和《ABP開發框架前後端開發系列---(8)ABP框架之Winform界面的開發過程》開始介紹了許可權管理的內容,其中只是列出了內部的許可權系統的審計和登陸信息,以及對Winform界面的整合,本篇隨筆繼續介紹ABP開... ...
  • 軟體開發中最常用的模式之一是緩存,這是一個簡單但非常有效的概念,想法是重用操作結果,執行繁重的操作時,我們會將結果保存在緩存容器中,下次我們需要該結果時,我們將從緩存容器中取出它,而不是再次執行繁重的操作。 例如,要獲得某人的頭像,您可能需要前往資料庫。我們不會每次都執行那次查詢,而是將結果保存在緩 ...
  • 在上一篇文章中學習了ABP的倉儲(Repository)功能,Repository對資料庫進行增刪改查操作。在這一篇文章中我們主要瞭解應用服務層。 應用服務用於將領域(業務)邏輯暴露給展現層。展現層通過傳入DTO(數據傳輸對象)參數來調用應用服務,而應用服務通過領域對象來執行相應的業務邏輯並且將DT... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...