[.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:
- =>:一個『=』,接著一個『>』符號,通常『=>』稱為goes to。也就是把左邊的東西,goes to 右邊的東西。
- =>的左邊:可以有(),也就是括號符號,也可以沒有。括號裡面,可以沒有東西(例如第3種例子),也可以有東西,且可以不只一個東西,例如第1種例子。
- =>的右邊:可以有一段運算式,例如第1, 2, 3種例子。可以有一段{}括起來可執行的區塊,例如第4種例子。可以有回傳,也可以沒有回傳東西。
先知道可以依據=>,將lambda拆成三個部分,讓我們繼續看下去。
說明
簡單的說,Lambda就是一種委派的匿名方法。而這一篇文章,我並不打算討論委派的細節。這篇只會帶到,lambda是怎麼從委派演進而來的。
接下來,我們會用C# 1.0, C# 2.0, C# 3.0,在使用委派時,是如何簡化成最後的lambda。
場景
媽媽要炒菜,給錢,買鹽。
C# 1.0的情況
- 先定義委派的簽章,也就是21行宣告delegate的部份。
- 在Cooking中,宣告一個getSalt的變數,其型別為剛剛定義的委派型別GetSalt。
- 媽媽將買鹽的邏輯,委派給Joey的BuySalt()方法來處理。
- 執行getSalt變數,取得鹽巴。
可以看到,需要四個步驟,重點在於:一定要定義一個實際的方法,不管是不是在媽媽身上,或是在別的Class上。不管是不是執行個體方法,還是靜態方法。總之,一定要定義一個實體的方法且有方法名字。
C# 2.0的情況
- 一樣先定義委派的簽章。
- 在C# 2.0中,不再一定需要某一個執行個體方法或靜態方法,來指定委派變數。而可以直接透過delegate(參數){委派執行內容}的方式,定義委派要執行的內容。
- 執行getSalt變數,取得鹽巴。
在C# 2.0,已經把原本四個步驟,簡化成三個。原因就在,委派的執行內容,不再一定需要依賴於某一個執行個體方法,或是靜態方法。而可以直接透過delegate關鍵字宣告。我們稱之為匿名方法。
C# 3.0的情況
一樣是三個步驟,但把匿名方法的部份更加簡化了。
- 不需要再透過delegate來宣告。
- 基本上不需要定義參數的型別,除非無法推斷。
- 只有一個參數時,參數不需要用小括號括起來。如果沒有參數,或兩個參數以上,則一定要用小括號刮起來。(一個參數時,也可以括)
結論
重新整理前言中的三個element:
- =>,代表這是個匿名方法。可以想像成宣告為delegate的匿名方法。意義是goes to,也就是把=>左邊的參數,交給=>右邊的運算式或陳述式執行。
- =>左邊,代表匿名方法的參數。可以沒有參數,可以多個參數,用『,』隔開。參數的位置,即為委派方法簽章的對應參數位置。參數的命名,可以不與委派方法簽章上相同,就如同C# 1.0的例子。這個參數的命名,只是供匿名方法內部所使用。
-
=>右邊,代表匿名方法的內容。可以使用=>左邊的參數,也可以使用匿名方法外部的變數。(兩者生命週期不同)
方法內容,可以沒有回傳值,例如委派的方法簽章上是回傳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);
}
}
}
結果:
補充:通常在設計委派,我們定義完委派的方法簽章後,會在class上宣告一個public的變數,或是在方法的參數上,來使用這個委派的型別。而此變數或參數,實際所要執行的方法內容,會交由外部來決定。這樣子設計,才會有彈性。在Mom的類別中,才不需要去考慮,究竟鹽巴是怎麼買回來的。
Reference
Sample Code
download: LambdaSample.zip
blog 與課程更新內容,請前往新站位置:http://tdd.best/