天天看點

[C# 基礎知識系列]專題十四:深入了解Lambda表達式

 引言:

   對于剛剛接觸Lambda表達式的朋友們,可能會對Lambda表達式感到非常疑惑,它到底是個什麼什麼樣的技術呢?以及它有什麼好處和先進的地方呢?下面的介紹将會解除你這些疑惑。

一、Lambda表達式的演變過程

對于Lambda表達式中都會使用這個運算符——“=>”,它讀成“goes to” ,該運算符的左邊為輸入參數,右邊是表達式或者語句塊,下面就看看Lambda表達式是如何來建立委托執行個體(代碼同時也給出了Lambda表達式從匿名方法的示範過程,進而幫助大家更好的了解Lambda表達式是匿名函數的概念,隻不過C#3 中提出的Lambda表達式比匿名函數的使用更加簡潔和直覺了,其實原理都是一樣的, 編譯器同樣會把Lambda表達式編譯成匿名函數,也就是一個名字的方法):

using System; 

namespace Lambda表達式Demo 

    class Program 

    { 

        /// <summary> 

        /// Lambda 表達式使用示範 

        /// </summary> 

        /// <param name="args"></param> 

        static void Main(string[] args) 

        { 

            // Lambda表達式的演變過程 

            // 下面是C# 1中建立委托執行個體的代碼 

            Func<string, int> delegatetest1 = new Func<string, int>(Callbackmethod); 

            //                                  ↓ 

            // C# 2中用匿名方法來建立委托執行個體,此時就不需要額外定義回調方法Callbackmethod 

            Func<string, int> delegatetest2 = delegate(string text) 

            { 

                return text.Length; 

            }; 

            // C# 3中使用Lambda表達式來建立委托執行個體 

            Func<string, int> delegatetest3 = (string text) => text.Length; 

            // 可以省略參數類型string,把上面代碼再簡化為: 

            Func<string, int> delegatetest4 = (text) => text.Length; 

            // 如果Lambda表達式隻需一個參數,并且那個參數可以隐式指定類型時, 

            // 此時可以把圓括号也省略,簡化為: 

            Func<string, int> delegatetest = text => text.Length; 

            // 調用委托 

            Console.WriteLine("使用Lambda表達式傳回字元串的長度為: " + delegatetest("learning hard")); 

            Console.Read(); 

        } 

        /// 回調方法 

        /// 如果使用了Lambda表達式和匿名函數,此方法就不需要額外定義 

        /// <param name="text"></param> 

        /// <returns></returns> 

        private static int Callbackmethod(string text) 

            return text.Length; 

    } 

運作結果為:

上面代碼中都有詳細的演變過程,這裡就不多解釋了,希望通過這部分之後,大家可以對Lambda表達式有進一步的了解,其實Lambda表達式就是匿名方法,其中使用Lambda表達式來建立委托執行個體,我們卻沒有指出建立的委托類型,其中編譯器會幫助我們去推斷委托類型,進而簡化我們建立委托類型所需要的代碼,進而更加簡潔,是以Lambda表達式可以總結為——它是在匿名方法的基礎上,再進一步地簡化了建立委托執行個體所需要的代碼。

二、Lambda表達式的使用

為了幫助大家更好的了解Lambda表達式,下面示範下用Lambda表達式來記錄事件(代碼中Lambda運算符的右邊調用了一個回調方法ReportEvent()):

using System.Windows.Forms; 

namespace Lambda表達式來記錄事件Demo 

            // 建立一個button執行個體 

            Button button1 = new Button() { Text ="點選我"}; 

            // C# 2中使用匿名方法來訂閱事件 

            //button1.Click+=delegate (object sender,EventArgs e) 

            //{ 

            //    ReportEvent("Click事件", sender, e); 

            //}; 

            //button1.KeyPress += delegate (object sender, KeyPressEventArgs e) 

            //    ReportEvent("KeyPress事件,即鍵盤按下事件", sender, e); 

            // C# 3Lambda表達式方式來訂閱事件 

            // 與上面使用匿名方法來訂閱事件是不是看出簡單了很多,并且也直覺了 

            button1.Click += (sender, e) => ReportEvent("Click事件", sender, e); 

            button1.KeyPress += (sender, e) => ReportEvent("KeyPress事件,即鍵盤按下事件", sender, e); 

            // C# 3之前初始化對象時使用下面代碼 

            //Form form = new Form(); 

            //form.Name = "在控制台中建立的窗體"; 

            //form.AutoSize = true; 

            //form.Controls.Add(button1); 

            // C# 3中使用對象初始化器 

            // 與上面代碼的比較中,也可以看出使用對象初始化之後代碼簡化了很多 

            Form form = new Form { Name = "在控制台中建立的窗體", AutoSize = true, Controls = { button1 } }; 

            // 運作窗體 

            Application.Run(form); 

        // 記錄事件的回調方法 

        private static void ReportEvent(string title, object sender, EventArgs e) 

            Console.WriteLine("發生的事件為:{0}", title); 

            Console.WriteLine("發生事件的對象為:{0}", sender); 

            Console.WriteLine("發生事件參數為: {0}", e.GetType()); 

            Console.WriteLine(); 

運作結果:

從上面代碼中可以看出,使用Lambda表達式之後代碼确實簡潔了很多,上面代碼中都有詳細的注釋,這裡就不解釋了,大家可以檢視代碼中的注釋來進行了解,并且代碼中注釋部分也列出了C# 3之前是如何實作這樣的代碼的, 這樣有利于比較,進而幫助大家更好的認識到Lambda所帶來的好處和進一步來了解Lambda表達式。

三、表達式樹

上面指出Lambda表達式除了可以用來建立委托外,C#編譯器還可以将他們轉換成表達式樹——用于表示Lambda表達式邏輯的一種資料結構,表達式樹也可以稱作表達式目錄樹,它将代碼表示成一個對象樹,而不是可執行的代碼。對于剛接觸哦表達式樹的朋友肯定會問——為什麼需要把Lambda表達式轉化為表達式目錄樹呢?對于表達式樹的提出主要是為後面Linq to SQL 做鋪墊,一個Linq to SQL 的查詢語句并不是在C#的程式中執行的,而是C#編譯器把它轉化為SQL 語句,然後再在資料庫中執行。在我們使用Linq to SQL的時候都需要添加一個Linq to SQL的類,該類的擴充名dbml,該的作用就是幫助我們把Linq to SQL 的語句映射為SQL語句,然後再在資料庫中執行SQL語句,把傳回的結果再傳回給一個IQueryable集合,是以Linq to SQL 也采用了通常的ORM(Object—Relationship—Mapping)來設計的,相當于是一個ORM架構,不過這個架構隻能與微軟的SQL server資料庫進行映射,對于其他類型的資料庫卻不可以,然而很多其他開發人員卻對此進行了一些擴充,擴充了對其他資料庫的支援。前不久還在部落格園中釋出了開源的Linq架構的,名字為ELinq,其他它就是對Linq to SQL的一個擴充,使Linq語句可以映射到其他資料庫的查詢語句。

下面先看看如何把Lambda表達式轉化為表達式目錄樹(其中需要引入一個新的命名空間—— System.Linq.Expressions):

// 引用額外的命名空間 

using System.Linq.Expressions; 

namespace 表達式樹Demo 

        /// 表達式樹的示範 

            #region 将Lambda表達式轉換為表達式樹示範 

            // 将Lambda表達式轉換為Express<T>的表達式樹 

            // 此時express不是可執行的代碼,它現在是一個表達式樹的資料結構 

            Console.WriteLine("将Lambda表達式轉化為表達式樹的示範:"); 

            Expression<Func<int, int, int>> expression = (a, b) => a + b; 

            // 獲得表達式樹的參數 

            Console.WriteLine("參數1: {0},參數2:{1}", expression.Parameters[0],expression.Parameters[1]); 

            // 既然叫做樹,那肯定有左右節點 

            // 擷取表達式樹的主體部分 

            BinaryExpression body = (BinaryExpression)expression.Body; 

            // 左節點,每個節點本身就是一個表達式對象 

            ParameterExpression left = (ParameterExpression)body.Left; 

            // 右節點 

            ParameterExpression right = (ParameterExpression)body.Right; 

            Console.WriteLine("表達式主體為:"); 

            Console.WriteLine(expression.Body); 

            Console.WriteLine("表達式樹左節點為:{0}{4} 節點類型為:{1}{4}{4} 表達式右節點為:{2}{4} 節點類型為:{3}{4}", left.Name, left.NodeType, right.Name, right.NodeType,Environment.NewLine); 

            #endregion  

            #region 把表達式樹轉化回可執行代碼 

            // Compile方法生成Lambda表達式的委托 

            Console.WriteLine("按下Enter鍵進入将表達式樹轉換為Lambda表達式的委托示範:"); 

            int result = expression.Compile()(2, 3); 

            Console.WriteLine("調用Lambda表達式委托結果為:" + result); 

            Console.ReadKey(); 

            #endregion 

上面代碼首先把Lambda表達式轉化為表達式樹,下面這行代碼就是把Lambda表達式轉化為表達式樹:

Expression<Func<int, int, int>> expression = (a, b) => a + b; 

之後對于表達式樹這種資料結構進行分析來獲得該樹中的主體和左右節點是什麼,獲得主體和左右節點的代碼如下:

// 擷取表達式樹的主體部分 

從上面代碼可以得出——樹中的每個節點都是一個表達式(ParameterExpression和BinaryExpression都是繼承Expression的,是以左右節點都是表達式),分析完表達式樹之後,代碼中還示範了如果把表達式樹轉化為可執行的代碼,即轉化為Lambda表達式的委托對象(此時調用Expression的Compile()方法來轉化為可執行代碼),通過調用委托來獲得結果。 

四、總結

 到這裡本專題的内容也介紹的差不多了,希望通過本專題使一些之前對Lambda表達式感到疑惑的朋友們現在可以了解Lambda表達式,因為隻有了解好Lambda表達式之後,對于Linq的學習就可以說是輕而易舉了。

<a href="http://down.51cto.com/data/2361951" target="_blank">附件:http://down.51cto.com/data/2361951</a>

     本文轉自LearningHard 51CTO部落格,原文連結:http://blog.51cto.com/learninghard/1087512,如需轉載請自行聯系原作者