[.NET]快快樂樂學LINQ系列前哨戰-Lambda的簡介

[.NET]快快樂樂學LINQ系列前哨戰-Lambda的簡介

前言

總算來到了看起來像是新東西的lambda了,什麼是lambda?舉個例子來說:

//第1種例子
(x, y) => x == y

//第2種例子
x => x + 1

//第3種例子
() => DoSomething()

//第4種例子
x => { x++; Console.WriteLine(x); return x; }

這種鬼東西就叫lambda。更要命的是,這個lambda竟然可以當做方法參數,或是塞給變數。

在說明之前,我想先把上面這類例子,抽象來看有哪一些元素,來組成lambda:

  1. =>:一個『=』,接著一個『>』符號,通常『=>』稱為goes to。也就是把左邊的東西,goes to 右邊的東西。
  2. =>的左邊:可以有(),也就是括號符號,也可以沒有。括號裡面,可以沒有東西(例如第3種例子),也可以有東西,且可以不只一個東西,例如第1種例子。
  3. =>的右邊:可以有一段運算式,例如第1, 2, 3種例子。可以有一段{}括起來可執行的區塊,例如第4種例子。可以有回傳,也可以沒有回傳東西。

先知道可以依據=>,將lambda拆成三個部分,讓我們繼續看下去。

 

說明

簡單的說,Lambda就是一種委派的匿名方法。而這一篇文章,我並不打算討論委派的細節。這篇只會帶到,lambda是怎麼從委派演進而來的。

接下來,我們會用C# 1.0, C# 2.0, C# 3.0,在使用委派時,是如何簡化成最後的lambda。

 

場景

媽媽要炒菜,給錢,買鹽。

 

C# 1.0的情況

image

  1. 先定義委派的簽章,也就是21行宣告delegate的部份。
  2. 在Cooking中,宣告一個getSalt的變數,其型別為剛剛定義的委派型別GetSalt。
  3. 媽媽將買鹽的邏輯,委派給Joey的BuySalt()方法來處理。
  4. 執行getSalt變數,取得鹽巴。

可以看到,需要四個步驟,重點在於:一定要定義一個實際的方法,不管是不是在媽媽身上,或是在別的Class上。不管是不是執行個體方法,還是靜態方法。總之,一定要定義一個實體的方法且有方法名字。

 

C# 2.0的情況

image

  1. 一樣先定義委派的簽章。
  2. 在C# 2.0中,不再一定需要某一個執行個體方法或靜態方法,來指定委派變數。而可以直接透過delegate(參數){委派執行內容}的方式,定義委派要執行的內容。
  3. 執行getSalt變數,取得鹽巴。

在C# 2.0,已經把原本四個步驟,簡化成三個。原因就在,委派的執行內容,不再一定需要依賴於某一個執行個體方法,或是靜態方法。而可以直接透過delegate關鍵字宣告。我們稱之為匿名方法。

 

C# 3.0的情況

image

一樣是三個步驟,但把匿名方法的部份更加簡化了。

  1. 不需要再透過delegate來宣告。
  2. 基本上不需要定義參數的型別,除非無法推斷。
  3. 只有一個參數時,參數不需要用小括號括起來。如果沒有參數,或兩個參數以上,則一定要用小括號刮起來。(一個參數時,也可以括)

結論

重新整理前言中的三個element:

  1. =>,代表這是個匿名方法。可以想像成宣告為delegate的匿名方法。意義是goes to,也就是把=>左邊的參數,交給=>右邊的運算式或陳述式執行。
  2. =>左邊,代表匿名方法的參數。可以沒有參數,可以多個參數,用『,』隔開。參數的位置,即為委派方法簽章的對應參數位置。參數的命名,可以不與委派方法簽章上相同,就如同C# 1.0的例子。這個參數的命名,只是供匿名方法內部所使用。
  3. =>右邊,代表匿名方法的內容。可以使用=>左邊的參數,也可以使用匿名方法外部的變數。(兩者生命週期不同)
    方法內容,可以沒有回傳值,例如委派的方法簽章上是回傳void。也可以有回傳值,看方法簽章上定義為何,以這例子來說,回傳型別為Salt。
    回傳型別也可以是泛型型別,那就是泛型委派。

簡單的說,Lambda,就是一種委派的匿名方法的簡化寫法。只要了解C# 1.0, C# 2.0到C# 3.0,委派在使用上的演進,自然在面對lambda上那堆奇怪的符號,奇怪的位置與宣告,奇怪的內容,就可以對照到原本的意義了。

最後,附上執行的例子:媽媽時代。

    class Program
    {
        static void Main(string[] args)
        {
            //媽媽時代
            var momGeneration = new List<Mom> { new Mom_CSharp_1(), new Mom_CSharp_2(), new Mom_CSharp_3() };

            Console.WriteLine("用120元買的鹽炒菜");
            MomsCooking(momGeneration, 120);
            Console.WriteLine();

            Console.WriteLine("用80元買的鹽炒菜");
            MomsCooking(momGeneration, 80);
            Console.WriteLine();
        }

        private static void MomsCooking(List<Mom> momGeneration, int money)
        {
            foreach (var mom in momGeneration)
            {
                mom.Cooking(money);
            }
        }
    }

結果:

image

補充:通常在設計委派,我們定義完委派的方法簽章後,會在class上宣告一個public的變數,或是在方法的參數上,來使用這個委派的型別。而此變數或參數,實際所要執行的方法內容,會交由外部來決定。這樣子設計,才會有彈性。在Mom的類別中,才不需要去考慮,究竟鹽巴是怎麼買回來的。

 

Reference

  1. Lambda 運算式 (C# 程式設計手冊)
  2. Delegates, Events, and Lambda Expressions
  3. 匿名函式 (C# 程式設計手冊)

Sample Code

download: LambdaSample.zip


blog 與課程更新內容,請前往新站位置:http://tdd.best/