香蕉视频app

Keep on going never give up.

Let's Go

C# 學習筆記(58)Lambda表達式

C#Lonely2019-10-03 15:50:0373次0條

Lambda表達式簡介

Lambda表達式可以理解為一個匿名方法,它可以包含表達式和語句,并且用于創建委托或轉換為表達式樹。在使用Lambda表達式時,都會使用“=>”運算符(讀作“goes to”),該運算符的左邊時匿名方法的輸入參數,右邊是表達式或語句塊。


Lambda表達式的演變過程

香蕉视频app 大家可以認為匿名方法就是Lambda表達式的“前世”,而Lambda則是匿名方法的投胎轉世,種種因果都與Lambda表達式的演變過程有關。下面代碼演示Lambda表達式從匿名方法一路演變而來的過程。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            //Lambda表達式的演變過程
            //下面是C# 1.0中創建委托實例的代碼
            Func<string, int> delegateTest_1 = new Func<string, int>(Callback);

            //C# 2.0中用匿名方法來創建委托實例,此時就不需要去額外定義回調方法Callback了
            Func<string, int> delegateTest_2 = delegate (string text) { return text.Length; };

            //C# 3.0中使用Lambda表達式創建委托實例
            Func<string, int> delegateTest_3 = (string text) => text.Length;

            //可以省略參數類型string,從而將代碼再次簡化
            Func<string, int> delegateTest_4 = (text) => text.Length;

            //再次簡化,此時圓括號()也可以省略掉
            Func<string, int> delegateTest_5 = text => text.Length;

            //調用委托
            Console.WriteLine("字符串長度為:{0}",delegateTest_5("你好,世界!"));
            Console.ReadKey();

        }

        //回調方法
        private static int Callback(string text)
        {
            return text.Length;
        }

    }
}

以上代碼演示了Lambda表達式的如何由匿名方法演變而來的過程,由此看出Lambda表達式十分簡潔。


Lambda表達式的使用

香蕉视频app 在實際開發過程中,委托的用途莫過于訂閱事件了,為了加深大家對Lambda表達式的理解,這里選擇演示用Lambda表達式去訂閱事件。

下面給出的是C# 3.0之前的訂閱代碼,以形成對比,代碼演示如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//添加對System.Windows.Forms.dll的引用并加入命名空間
using System.Windows.Forms;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Button btn = new Button();
            btn.Text = "按鈕";

            //C# 2.0中使用匿名方法來訂閱事件
            btn.Click += delegate (object sender, EventArgs e)
            {
                ReportEvent("Click事件",sender,e);
            };

            btn.KeyPress += delegate (object sender, KeyPressEventArgs e)
            {
                ReportEvent("KeyPress事件", sender, e);
            };

            //在C# 3.0之前,初始化對象會使用以下代碼
            Form frm = new Form();
            frm.Text = "控制臺中創建出來的窗體";
            frm.AutoSize = true;
            frm.Controls.Add(btn);
            //運行窗體
            Application.Run(frm);

        }

        //記錄事件的回調方法
        private static void ReportEvent(string title,object sender,EventArgs e)
        {
            Console.WriteLine("發生事件:{0}",title);
            Console.WriteLine("發生事件對象:{0}",sender);
            Console.WriteLine("發生事件參數:{0}\n\n",e.GetType());
        }

    }
}

以上是C# 3.0之前的實現方式,運行結果如下所示:

image.png


看完了C# 3.0之前的實現代碼,我們再來用C# 3.0特性實現同樣的效果,C# 3.0的實現代碼如下所示:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//添加對System.Windows.Forms.dll的引用并加入命名空間
using System.Windows.Forms;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Button btn = new Button() { Text = "按鈕" };

            //C# 3.0采用Lambda表達式的方式來訂閱事件
            btn.Click += (sender, e) => ReportEvent("Click事件", sender, e);
            btn.KeyPress += (sender, e) => ReportEvent("KeyPress事件", sender, e); ;

            //在C# 3.0中使用對象集合初始化器
            Form frm = new Form()
            {
                Text = "控制臺中創建出來的窗體",
                AutoSize = true,
                Controls = { btn }
            };

            //運行窗體
            Application.Run(frm);

        }

        //記錄事件的回調方法
        private static void ReportEvent(string title,object sender,EventArgs e)
        {
            Console.WriteLine("發生事件:{0}",title);
            Console.WriteLine("發生事件對象:{0}",sender);
            Console.WriteLine("發生事件參數:{0}\n\n",e.GetType());
        }

    }
}

以上代碼可以看出,使用C# 3.0的對象集合初始化器和Lambda表達式后,代碼確實簡潔不少。這里,委托可以用Lambda表達式來實例化,去除了多余大括號代碼。Lambda表達式的使用可以明顯減少代碼的書寫量,從而有利于開發人員更好的維護代碼,理清程序的結構。


表達式樹

Lambda表達式除了可以用來創建委托外,還可以轉換成表達式樹。表達式樹(或稱“表達式目錄樹”)是用來表示Lambda表達式邏輯的一種數據結構,它將代碼表示成一個對象樹,而非可執行的代碼。


動態構造一個表達式樹

代碼示例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//引入Expressions<TDelegate>類的命名空間
using System.Linq.Expressions;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            // 構造“a+b”的表達式結構

            //表達式參數
            //ParameterExpression 表示一個命名的參數表達式。
            //Expression 提供一種基類,表示表達式樹節點的類派生自該基類。 它還包含用來創建各種節點類型的 static(在 Visual Basic 中為 Shared)工廠方法。這是一個 abstract 類
            //Expression.Parameter(Type type, string name) 創建一個 ParameterExpression 節點,該節點可用于標識表達式樹中的參數或變量。
            //參數:
            //type: 參數或變量的類型。 name:僅用于調試或打印目的的參數或變量的名稱。
            ParameterExpression a = Expression.Parameter(typeof(int),"a");

            ParameterExpression b = Expression.Parameter(typeof(int), "b");

            //表達式樹的主體部分
            //BinaryExpression 表示具有二進制運算符的表達式。
            //Expression.Add(Expression left, Expression right) 創建一個表示不進行溢出檢查的算術加法運算的 BinaryExpression。
            BinaryExpression be = Expression.Add(a,b);

            //構造表達式樹
            //Expression<> 將強類型化的 Lambda 表達式表示為表達式樹形式的數據結構。 此類不能被繼承。
            //Expression.Lambda<>() 創建一個在編譯時委托類型已知的 Expressions<TDelegate>
            Expression<Func<int, int, int>> expressionTree = Expression.Lambda<Func<int, int, int>>(be,a,b);

            //分析樹結構,獲取表達式樹的主體部分
            BinaryExpression body = (BinaryExpression)expressionTree.Body;

            //左節點,每一個節點本身就是一個表達式對象
            ParameterExpression left = (ParameterExpression)body.Left;

            //右節點
            ParameterExpression right = (ParameterExpression)body.Right;

            Console.WriteLine("表達式結構為:{0}",expressionTree.Body);
            Console.WriteLine();
            Console.WriteLine("表達式左節點為:{0},節點類型為:{1}",left.Name,left.Type);
            Console.WriteLine();
            Console.WriteLine("表達式右節點為:{0},節點類型為:{1}", right.Name, right.Type);
            Console.ReadKey();

        }


    }
}

以上代碼演示了通過一個表達式動態地構造表達式樹對象,然后輸出表達式樹的結構、主體、和左右節點的過程,運行結果如下所示:

image.png

前面代碼所構造的表達式樹結構可以用下圖來更形象地表示,由圖可以看出表達式樹也是一種樹形數據結構,該數據結構可以很好地描述Lambda表達式的邏輯。

image.png


通過Lambda表達式來構造表達式樹

香蕉视频app 前面代碼演示了動態地構造表達式樹的方法,除此之外,還可以直接使用Lambda表達式來構造表達式樹,具體構造過程如下所示:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//引入Expressions<TDelegate>類的命名空間
using System.Linq.Expressions;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            // 構造“a+b”的表達式結構

            //將Lambda表達式構造成表達式樹
            Expression<Func<int, int, int>> expressionTree = (a, b) => a + b;

            //獲得表達式樹參數
            Console.WriteLine("參數1:{0},參數2:{1}",expressionTree.Parameters[0],expressionTree.Parameters[1]);
            Console.WriteLine();

            //獲取表達式樹的主體部分
            BinaryExpression body = (BinaryExpression)expressionTree.Body;

            //左節點,每一個節點本身就是一個表達式對象
            ParameterExpression left = (ParameterExpression)body.Left;

            //右節點
            ParameterExpression right = (ParameterExpression)body.Right;

            Console.WriteLine("表達式結構為:{0}",expressionTree.Body);
            Console.WriteLine();
            Console.WriteLine("表達式左節點為:{0},節點類型為:{1}",left.Name,left.Type);
            Console.WriteLine();
            Console.WriteLine("表達式右節點為:{0},節點類型為:{1}", right.Name, right.Type);
            Console.ReadKey();

        }


    }
}

從以上代碼可以看出,通過Lambda表達式來構造表達式樹的過程非常簡單,只需要把Lambda表達式賦給一個表達式樹變量就可以了。構造完一個表達式樹后,由于表達式對象并不是可執行代碼,它只是一個樹形數據結構,所以需要在代碼中再解析樹結果,分別獲得樹的參數和左右節點,然后輸出,運行結果如下所示:

image.png


如何把表達式樹轉換成可執行代碼

香蕉视频app 看完前面的代碼,你肯定會問:“表達式樹是一種樹形數據結構,但最終還是需要得到代碼的執行結果的,有沒有一種方式可以把表達式樹轉換成可執行代碼,然后輸出執行結果呢?”,下面就用代碼來解釋這個問題。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//引入Expressions<TDelegate>類的命名空間
using System.Linq.Expressions;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {

            //將Lambda表達式構造成表達式樹
            Expression<Func<int, int, int>> expressionTree = (a, b) => a + b;

            //通過調用Compile()方法來 lambda 表達式的委托。
            Func<int, int, int> del = expressionTree.Compile();

            //調用委托實例,獲得結果
            int result = del(5,10); //result為15

            Console.WriteLine("5 + 15 的和為:{0}", result);
            Console.ReadKey();

        }


    }
}

以上代碼通過Expressions<TDelegate>類的Compile()方法將表達式樹編譯成委托實例,然后通過委托調用的方式得到兩個數的和。



暗錨,解決錨點偏移

文章評論

    嘿,來試試登錄吧!