C# Lambda


介紹  

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

通過使用 lambda 表達式,可以寫入可作為參數傳遞或作為函數調用值返回的本地函數。 Lambda 表達式對於編寫 LINQ 查詢表達式特別有用。

Lambda表達式本身可划分為兩種類型:語句Lambda和表達式Lambda

若要創建 Lambda 表達式,需要在 Lambda 運算符 => (goes to)左側指定輸入參數(如果有),然后在另一側輸入表達式或語句塊。

格式

(input parameters) => expression

或者 

(input parameters) => {statement;}

左邊參數列表可以有多個參數,一個參數,或者無參數;參數類型可以隱式或者顯式,參數類型可以忽略,因為可以根據使用的上下文進行推斷而得到。

僅當 lambda 只有一個輸入參數時,括號才是可選的;否則括號是必需的。

例如:
                (x, y) => x * y                              //多參數,隱式類型=> 表達式
                x => x * 10                                  //單參數,隱式類型=>表達式
                x => { return x * 10; }                   //單參數,隱式類型=>語句塊
                (int x) => x * 10                            //單參數,顯式類型=>表達式
                (int x) => { return x * 10; }            //單參數,顯式類型=>語句塊
                () => Console.WriteLine()                //無參數

例如,lambda 表達式 x => x * x 指定名為 x 的參數並返回 x 的平方值。 

Lambda表達式與委托類型

如下面的示例所示,你可以將此表達式分配給委托類型:

delegate int del(int i); 
static void Main(string[] args) 
{ 
    del myDelegate = x => x * x; 
    int j = myDelegate(5); //j = 25 
}

=> 運算符具有與賦值運算符 (=) 相同的優先級並且是右結合運算

使用 Lambda 表達式創建該委托最為方便

 Lambda表達式可以被轉成委托類型,但必須滿足以下幾點: 
        1.兩者參數個數相同 
        2.參數類型相同,注意隱式類型要參與類型辨析 
        3.委托的返回類型要與Lambda的相同,不論是表達式還是語句塊

Lambda與匿名函數的對比:

public delegate int DelegateTest(int n1, int n2);
        static void Main(string[] args)
        {
            //委托:方法作為參數傳遞
            var r1 = Result(3, 4, Sum);  
            //使用匿名方法傳遞委托
            var r4 = Result(3,4,delegate(int x,int y){return x+y;});
            //語句lambda傳遞委托
            var r2 = Result(3, 4, (a, b) => { return a - b; }); 
            //lambda傳遞委托
            var r3 = Result(3, 4, (a, b) => a * b); 
            Console.ReadLine();
        }
        public static int Result(int a, int b, DelegateTest @delegate)
        {
            return @delegate(a, b);
        }
        public static int Sum(int a, int b)
        {
            return a + b;
        }
View Code

Func和Lambda

更加簡化了代碼量,此時就不用像上面一樣就行聲明委托直接可以用

public static void LambdaFunc()
        {
            Func<string, string, string> getFunc = (p1, p2) =>
            {
                return p1 + "    " + p2;
            };
          Console.WriteLine(getFunc("我是第一個參數","我是第二個參數"));
        }

Linq和Lambda

   //獲取所有的用戶ID

            List<string> gidList = Users.Select(p => p.Gid).ToList();

           //獲取所有大於6的集合

            aaList = retList.Where(p => p > 6).ToList();

異步 lambda

通過使用 async 和 await 關鍵字,你可以輕松創建包含異步處理的 lambda 表達式和語句。 例如,下面的 Windows 窗體示例包含一個調用和等待異步方法 ExampleMethodAsync的事件處理程序。

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);
    }
}
View Code

Lambda表達式的內部機制

Lambda表達式並非CLR內部的固有構造,它們的實現是由C#編譯器在編譯時生成的。Lambda表達式為“以內嵌方式聲明委托”模式提供一個對應的C#與語言構造。所以,當我們編寫lambda時,編譯器實際上會生成一系列代碼,就以上面的代碼為例,通過反編譯工具查看生成的等價代碼:

Main方法中使用lambda表達式傳遞委托,編譯后生成的是匿名函數。這也證明了上面說的lambda其實就是簡化了的匿名函數

再來看看上面的匿名方法 delegate(int x,int y){return x+y;}和lambad表達式(a, b) => { return a - b; }、(a, b) => a * b),編譯后實際生成了3個靜態方法 <Main>b_0,<Main>b_1和<Main>b_2,也就是說,在調用的時候還是由這3個靜態方法去分別實例化成委托,並作為參數傳遞的,又回到了最初對委托的了解:委托實現把方法作為參數傳入到另一個方法

表達式樹

表達式樹是一種允許將lambda表達式表示為樹狀數據結構而不是可執行邏輯的代碼。

表達式樹的創建:

1.通過Lambda表達式創建表達式樹

 下面的代碼演示將lambda表達式表示為可執行代碼和表達式樹:

   Func<int, int> res = x => x + 1;               //Code 
   Expression<Func<int, int>> exp = x => x + 1;   //Data

進行上面的賦值之后,委托res引用返回x+1的方法,表達式樹exp引用描述表達式x=>x+1的數據結構,這是兩者的明顯區別。

2.通過API創建表達式樹

    要使用API創建表達式樹,需要使用Expression類。該類提供創建特定類型的表達式樹節點的靜態方法,例如:ParameterExpression(表示一個變量或參數)或MethodCallExpression(表示一個方法調用)。下面演示使用API創建一個表達式樹:

static void Main(string[] args)
        {//創建表達式樹:Expression<Func<int, int>> exp = x => x + 1;
            ParameterExpression param = Expression.Parameter(typeof(int),"x");
            ConstantExpression value = Expression.Constant(1, typeof(int));
            BinaryExpression body = Expression.Add(param, value);
            Expression<Func<int, int>> lambdatree = Expression.Lambda<Func<int, int>>(body, param);
            Console.WriteLine("參數param:{0}", param);
            Console.WriteLine("描述body:{0}", body);
            Console.WriteLine("表達式樹:{0}", lambdatree);

            //解析表達式樹:
            //取得表達式樹的參數
            ParameterExpression dparam = lambdatree.Parameters[0] as ParameterExpression;
            //取得表達式樹描述
            BinaryExpression dbody = lambdatree.Body as BinaryExpression;
            //取得節點
            ParameterExpression left = dbody.Left as ParameterExpression;
            ConstantExpression right = body.Right as ConstantExpression;
            Console.WriteLine("解析出的表達式:{0}=>{1} {2} {3}", param.Name, left.Name, body.NodeType, right.Value);
            Console.ReadLine();
        }
View Code

運行結果:

關於表達式樹:表達式樹這一概念的引入,使得程序可以將一個lambda表達式編譯成數據來表示,而不是編譯成一個表示為委托的具體實現(靜態方法)。利用這一特性,Linq to Sql和Linq to Xml等庫能解釋表達式樹,並在CIL之外的上下文中使用。

 

更多參考:

Lambda 表達式(C# 編程指南)

C#之Lambda不得不說的用法

Lambda表達式和表達式樹

快樂的Lambda表達式(一)

 


免責聲明!

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



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