C# Lambda 表達(dá)式

聲明

本文內(nèi)容來自微軟 MVP solenovex 的視頻教程——真會C#? - 第4章 委托挨稿、事件、Lambda表達(dá)式(完結(jié))寇壳,大致和第 4 課—— 4.3 4.4 Lambda表達(dá)式 對應(yīng)。可在 GitHub 中查看 C# 視頻教程的配套PPT

本文主要包括以下內(nèi)容:

  1. 顯式指定 Lambda 表達(dá)式的參數(shù)類型
  2. 捕獲外部變量
  3. Lambda 表達(dá)式 vs 本地方法
  4. 匿名方法

Lambda 表達(dá)式

Lambda表達(dá)式其實就是一個用來代替委托實例的未命名的方法鲤桥,編譯器會把Lambda表達(dá)式轉(zhuǎn)化為以下二者之一:

  • 一個委托實例
  • 一個表達(dá)式樹(expression tree),類型是 Expression<TDelegate>渠概,它表示了可遍歷的對象模型中 Lambda 表達(dá)式里面的代碼茶凳。它允許 Lambda 表達(dá)式延遲到運行時再被解釋
delegate int Transformer (int i);

Transformer sqr = x => x * x;
Console.WriteLine (sqr(3)); // 9

實際上,編譯器會通過編寫一個私有方法來解析這個 Lambda 表達(dá)式播揪,然后把表達(dá)式的代碼移動到這個方法里贮喧。

Lambda表達(dá)式的形式,(參數(shù))=> 表達(dá)式或語句塊猪狈,(parameters) => expression-or-statement-block其中如果只有一個參數(shù)并且類型可推斷的話箱沦,那么參數(shù)的小括號可以省略

Lambda 表達(dá)式與委托,每個 Lambda 表達(dá)式的參數(shù)對應(yīng)委托的參數(shù)雇庙,表達(dá)式的類型對應(yīng)委托的返回類型谓形。

x => x * x;

delegate int Transformer (int i);

Lambda 表達(dá)式的代碼也可以是語句塊。x => { return x * x; };

Lambda 表達(dá)式通常與 Func 和 Action 委托一起使用疆前,

Func<int,int> sqr = x => x * x;

Func<string,string,int> totalLength = (s1, s2) => s1.Length + s2.Length;
int total = totalLength ("hello", "world"); // total is 10;

顯式指定 Lambda 表達(dá)式的參數(shù)類型

void Foo<T> (T x) {}
void Bar<T> (Action<T> a) {}

Bar (x => Foo (x)); // What type is x?

Bar ((int x) => Foo (x));

Bar<int> (x => Foo (x)); // Specify type parameter for Bar
Bar<int> (Foo); // As above, but with method group

捕獲外部變量

Lambda 表達(dá)式可以引用本地的變量和所在方法的參數(shù)寒跳。

static void Main()
{
    int factor = 2;
    Func<int, int> multiplier = n => n * factor;
    Console.WriteLine (multiplier (3)); // 6
}

被捕獲的變量

被 Lambda 表達(dá)式引用的外部變量叫做被捕獲的變量(captured variables)。捕獲了外部變量的 Lambda 表達(dá)式叫做閉包峡继。被捕獲的變量是在委托被實際調(diào)用的時候才被計算冯袍,而不是在捕獲的時候。

int factor = 2;
Func<int, int> multiplier = n => n * factor;
factor = 10;
Console.WriteLine (multiplier (3)); // 30

Lambda 表達(dá)式本身也可以更新被捕獲的變量碾牌。

int seed = 0;
Func<int> natural = () => seed++;
Console.WriteLine (natural()); // 0
Console.WriteLine (natural()); // 1
Console.WriteLine (seed); // 2

被捕獲的變量的生命周期會被延長到和委托一樣康愤。

static Func<int> Natural()
{
    int seed = 0;
    return () => seed++; // Returns a closure
}

static void Main()
{
    Func<int> natural = Natural();
    Console.WriteLine (natural()); // 0
    Console.WriteLine (natural()); // 1
}

Lambda 表達(dá)式內(nèi)的本地變量

在 Lambda 表達(dá)式內(nèi)實例化的本地變量對于委托實例的每次調(diào)用來說都是唯一的。

static Func<int> Natural()
{
    return() => { int seed = 0; return seed++; };
}

static void Main()
{
    Func<int> natural = Natural();
    Console.WriteLine (natural()); // 0
    Console.WriteLine (natural()); // 0
}

捕獲迭代變量

當(dāng)捕獲 for 循環(huán)的迭代變量時舶吗,C# 會把這個變量當(dāng)作是在循環(huán)外部定義的變量征冷,這就意味著每次迭代捕獲的都是同一個變量。

Action[] actions = new Action[3];
for (int i = 0; i < 3; i++)
actions [i] = () => Console.Write (i);
foreach (Action a in actions) a(); // 333

Action[] actions = new Action[3];
int i = 0;
actions[0] = () => Console.Write (i);
i = 1;
actions[1] = () => Console.Write (i);
i = 2;
actions[2] = () => Console.Write (i);
i = 3;
foreach (Action a in actions) a(); // 333
如何解決每次迭代捕獲的都是同一個變量
Action[] actions = new Action[3];

for (int i = 0; i < 3; i++)
{
    int loopScopedi = i;
    actions [i] = () => Console.Write (loopScopedi);
}

foreach (Action a in actions) a(); // 012

注意:foreach誓琼,C#4检激,和 C#5+ 的區(qū)別。

Action[] actions = new Action[3];
int i = 0;

foreach (char c in "abc")
    actions [i++] = () => Console.Write (c);

foreach (Action a in actions) a(); // ccc in C# 4.0

Lambda 表達(dá)式 vs 本地方法

本地方法是 C#7 的一個新特性腹侣。它和 Lambda 表達(dá)式在功能上有很多重復(fù)之處叔收,但它有三個優(yōu)點:

  1. 可以簡單明了的進(jìn)行遞歸
  2. 無需指定委托類型(那一堆代碼)
  3. 性能開銷略低一點

本地方法效率更高是因為它避免了委托的間接調(diào)用(需要 CPU 周期,內(nèi)存分配)傲隶。本地方法也可以訪問所在方法的本地變量饺律,而且無需編譯器把被捕獲的變量 hoist 到隱藏的類。

匿名方法

匿名方法 vs Lambda 表達(dá)式

匿名方法和Lambda表達(dá)式很像跺株,但是缺少以下三個特性:

  1. 隱式類型參數(shù)
  2. 表達(dá)式語法(只能是語句塊)
  3. 編譯表達(dá)式樹的能力复濒,通過賦值給 Expression<T>
delegate int Transformer (int i);

Transformer sqr = delegate (int x) {return x * x;};
Console.WriteLine (sqr(3));

Transformer sqr = (int x) => {return x * x;};

// Or simply:
Transformer sqr = x => x * x;

其它

捕獲外部變量的規(guī)則和 Lambda 表達(dá)式是一樣的脖卖。但匿名方法可以完全省略參數(shù)聲明,盡管委托需要參數(shù)巧颈。public event EventHandler Clicked = delegate { };這就避免了觸發(fā)事件前的null檢查畦木。

// Notice that we omit the parameters:
Clicked += delegate { Console.WriteLine ("clicked"); };
Lambda

參考

Lambda expressions (C# Programming Guide)
=> operator (C# reference)
Anonymous functions (C# Programming Guide)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市砸泛,隨后出現(xiàn)的幾起案子十籍,更是在濱河造成了極大的恐慌,老刑警劉巖晾嘶,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件妓雾,死亡現(xiàn)場離奇詭異娶吞,居然都是意外死亡垒迂,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進(jìn)店門妒蛇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來机断,“玉大人,你說我怎么就攤上這事绣夺±艏椋” “怎么了?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵陶耍,是天一觀的道長奋蔚。 經(jīng)常有香客問我,道長烈钞,這世上最難降的妖魔是什么泊碑? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮毯欣,結(jié)果婚禮上馒过,老公的妹妹穿的比我還像新娘。我一直安慰自己酗钞,他們只是感情好腹忽,可當(dāng)我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著砚作,像睡著了一般窘奏。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上葫录,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天着裹,我揣著相機與錄音,去河邊找鬼压昼。 笑死求冷,一個胖子當(dāng)著我的面吹牛瘤运,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播匠题,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼拯坟,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了韭山?” 一聲冷哼從身側(cè)響起郁季,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎钱磅,沒想到半個月后梦裂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡盖淡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年年柠,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片褪迟。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡冗恨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出味赃,到底是詐尸還是另有隱情掀抹,我是刑警寧澤,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布心俗,位于F島的核電站傲武,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏城榛。R本人自食惡果不足惜揪利,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望吠谢。 院中可真熱鬧土童,春花似錦、人聲如沸工坊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽王污。三九已至罢吃,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間昭齐,已是汗流浹背尿招。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人就谜。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓怪蔑,卻偏偏與公主長得像,于是被迫代替她去往敵國和親丧荐。 傳聞我的和親對象是個殘疾皇子缆瓣,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,490評論 2 348

推薦閱讀更多精彩內(nèi)容