Lambda表達式簡介
Lambda表達式可以理解為一個匿名方法,它可以包含表達式和語句,并且用于創建委托或轉換為表達式樹。在使用Lambda表達式時,都會使用“=>”運算符(讀作“goes to”),該運算符的左邊時匿名方法的輸入參數,右邊是表達式或語句塊。
Lambda表達式的演變過程
大家可以認為匿名方法就是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之前的實現方式,運行結果如下所示:
看完了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(); } } }
以上代碼演示了通過一個表達式動態地構造表達式樹對象,然后輸出表達式樹的結構、主體、和左右節點的過程,運行結果如下所示:
前面代碼所構造的表達式樹結構可以用下圖來更形象地表示,由圖可以看出表達式樹也是一種樹形數據結構,該數據結構可以很好地描述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”的表達式結構 //將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表達式賦給一個表達式樹變量就可以了。構造完一個表達式樹后,由于表達式對象并不是可執行代碼,它只是一個樹形數據結構,所以需要在代碼中再解析樹結果,分別獲得樹的參數和左右節點,然后輸出,運行結果如下所示:
如何把表達式樹轉換成可執行代碼
香蕉视频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()方法將表達式樹編譯成委托實例,然后通過委托調用的方式得到兩個數的和。
文章評論