一区二区久久-一区二区三区www-一区二区三区久久-一区二区三区久久精品-麻豆国产一区二区在线观看-麻豆国产视频

從.NET中委托寫法的演變談開去(中):Lambda表達(dá)式及其優(yōu)勢(shì)

上一篇文章中我們簡(jiǎn)單探討了.NET 1.x和.NET 2.0中委托表現(xiàn)形式的變化,以及.NET 2.0中匿名方法的優(yōu)勢(shì)、目的及注意事項(xiàng)。那么現(xiàn)在我們來(lái)談一下.NET 3.5(C# 3.0)中,委托的表現(xiàn)形式又演變成了什么樣子,還有什么特點(diǎn)和作用。

.NET 3.5中委托的寫法(Lambda表達(dá)式)

Lambda表達(dá)式在C#中的寫法是“arg-list => expr-body”,“=>”符號(hào)左邊為表達(dá)式的參數(shù)列表,右邊則是表達(dá)式體(body)。參數(shù)列表可以包含0到多個(gè)參數(shù),參數(shù)之間使用逗號(hào)分割。例如,以下便是一個(gè)使用Lambda表達(dá)式定義了委托的示例1

Func<int, int, int> max = (int a, int b) =>{    if (a > b)    {        return a;    }    else    {        return b;    }};

與上文使用delegate定義匿名方法的作用相同,Lambda表達(dá)式的作用也是為了定義一個(gè)匿名方法。因此,下面使用delegate的代碼和上面是等價(jià)的:

Func<int, int, int> max = delegate(int a, int b){    if (a > b)    {        return a;    }    else    {        return b;    }};

那么您可能就會(huì)問(wèn),這樣看來(lái)Lambda表達(dá)式又有什么意義呢?Lambda表達(dá)式的意義便是它可以寫的非常簡(jiǎn)單,例如之前的Lambda表達(dá)式可以簡(jiǎn)寫成這樣:

Func<int, int, int> max = (a, b) =>{    if (a > b)    {        return a;    }    else    {        return b;    }};

由于我們已經(jīng)注明max的類型是Func<int, int, int>,因此C#編譯器可以明確地知道a和b都是int類型,于是我們就可以省下參數(shù)之前的類型信息。這個(gè)特性叫做“類型推演”,也就是指編譯器可以自動(dòng)知道某些成員的類型2。請(qǐng)不要輕易認(rèn)為這個(gè)小小的改進(jìn)意義不大,事實(shí)上,您會(huì)發(fā)現(xiàn)Lambda表達(dá)式的優(yōu)勢(shì)都是由這一點(diǎn)一滴的細(xì)節(jié)構(gòu)成的。那么我們?cè)賮?lái)一次改變:

Func<int, int, int> max = (a, b) => a > b ? a : b;

如果Lambda表達(dá)式的body是一個(gè)表達(dá)式(expression),而不是語(yǔ)句(statement)的話,那么它的body就可以省略大括號(hào)和return關(guān)鍵字。此外,如果Lambda表達(dá)式只包含一個(gè)參數(shù)的話,則參數(shù)列表的括號(hào)也可以省略,如下:

Func<int, bool> positive = a => a > 0;

如今的寫法是不是非常簡(jiǎn)單?那么我們來(lái)看看,如果是使用delegate關(guān)鍵字來(lái)創(chuàng)建的話會(huì)成為什么樣子:

Func<int, bool> positive = delegate(int a){    return a > 0;};

您馬上就可以意識(shí)到,這一行和多行的區(qū)別,這幾個(gè)關(guān)鍵字和括號(hào)的省略,會(huì)使得編程世界一下子變得大為不同。

當(dāng)然,Lambda表達(dá)式也并不是可以完全替代delegate寫法,例如帶ref和out關(guān)鍵字的匿名方法,就必須使用.NET 2.0中的delegate才能構(gòu)造出來(lái)了。

使用示例一

Lambda表達(dá)式的增強(qiáng)在于“語(yǔ)義”二字。“語(yǔ)義”是指代碼所表現(xiàn)出來(lái)的含義,說(shuō)的更通俗一些,便是指一段代碼給閱讀者的“感覺(jué)”如何。為了說(shuō)明這個(gè)例子,我們還是使用示例來(lái)說(shuō)明問(wèn)題。

第一個(gè)例子是這樣的:“請(qǐng)寫一個(gè)方法,輸入一個(gè)表示整型的字符串列表,并返回一個(gè)列表,包含其中偶數(shù)的平方,并且需要按照平方后的結(jié)果排序”。很簡(jiǎn)單,不是嗎?相信您一定可以一蹴而就:

static List<int> GetSquaresOfPositive(List<string> strList){    List<int> intList = new List<int>();    foreach (var s in strList) intList.Add(Int32.Parse(s));    List<int> evenList = new List<int>();    foreach (int i in intList)    {        if (i % 2 == 0) evenList.Add(i);    }    List<int> squareList = new List<int>();    foreach (int i in evenList) squareList.Add(i * i);    squareList.Sort();    return squareList;}

我想問(wèn)一下,這段代碼給您的感覺(jué)是什么?它給我的感覺(jué)是:做了很多事情。有哪些呢?

  1. 新建一個(gè)整數(shù)列表intList,把參數(shù)strList中所有元素轉(zhuǎn)化為整型保存起來(lái)。
  2. 新建一個(gè)整數(shù)列表evenList,把intList中的偶數(shù)保存起來(lái)。
  3. 新建一個(gè)整數(shù)列表squareList,把evenList中所有數(shù)字的平方保存起來(lái)。
  4. 將squareList排序。
  5. 返回squareList。

您可能會(huì)問(wèn):“當(dāng)然如此,還能怎么樣?”。事實(shí)上,如果使用了Lambda表達(dá)式,代碼就簡(jiǎn)單多了:

static List<int> GetSquaresOfPositiveByLambda(List<string> strList){    return strList        .Select(s => Int32.Parse(s)) // 轉(zhuǎn)成整數(shù)        .Where(i => i % 2 == 0) // 找出所有偶數(shù)        .Select(i => i * i) // 算出每個(gè)數(shù)的平方        .OrderBy(i => i) // 按照元素自身排序        .ToList(); // 構(gòu)造一個(gè)List}

配合.NET 3.5中定義的擴(kuò)展方法,這段代碼可謂“一氣呵成”(在實(shí)際編碼過(guò)程中,老趙更傾向于把這種簡(jiǎn)短的“遞進(jìn)式”代碼寫作一行)。那么這行代碼的“語(yǔ)義”又有什么變化呢?在這里,“語(yǔ)義”的變化在于代碼的關(guān)注點(diǎn)從“怎么做”變成了“做什么”。這就是Lambda表達(dá)式的優(yōu)勢(shì)。

在第一個(gè)方法中,我們構(gòu)造了多個(gè)容器,然后做一些轉(zhuǎn)化,過(guò)濾,并且向容器填充內(nèi)容。其實(shí)這些都是“怎么做”,也就是所謂的“how (to do)”。但是這些代碼并不能直接表示我們想要做的事情,我們想要做的事情其實(shí)是“得到XXX”,“篩選出YYY”,而不是“創(chuàng)建容器”,“添加元素”等操作。

在使用Lambda表達(dá)式的實(shí)現(xiàn)中,代碼變得“聲明式(declarative)”了許多。所謂“聲明式”,便是“聲稱代碼在做什么”,而不像“命令式(imperative)”的代碼在“操作代碼怎么做”。換句話說(shuō),“聲明式”關(guān)注的是“做什么”,是指“what (to do)”。上面這段聲明式的代碼,其語(yǔ)義則變成了:

  1. 把字符串轉(zhuǎn)化為整數(shù)
  2. 篩選出所有偶數(shù)
  3. 把每個(gè)偶數(shù)平方一下
  4. 按照平方結(jié)果自身排序 
  5. 生成一個(gè)列表

至于其中具體是怎么實(shí)現(xiàn)的,有沒(méi)有構(gòu)造新的容器,又是怎么向容器里添加元素的……這些細(xì)節(jié),使用Lambda表達(dá)式的代碼一概不會(huì)關(guān)心——這又不是我們想要做的事情,為什么要關(guān)心它呢?

雖然擴(kuò)展方法功不可沒(méi),但我認(rèn)為,Lambda表達(dá)式在這里的重要程度尤勝前者,因?yàn)樗?fù)責(zé)了最關(guān)鍵的“語(yǔ)義”。試想,“i => i * i”給您的感覺(jué)是什么呢?是構(gòu)造了一個(gè)委托嗎(當(dāng)然,您一定知道在這里其實(shí)構(gòu)造了一個(gè)匿名方法)?至少對(duì)我來(lái)說(shuō),它的含義是“把i變成i * i”;同樣,“i => i % 2 == 0”給我的感覺(jué)是“(篩選標(biāo)準(zhǔn)為)i模2等于零”,而不是“構(gòu)造一個(gè)委托,XXX時(shí)返回true,否則返回false”;更有趣的是,OrderBy(i => i)給我的感覺(jué)是“把i按照i自身排序”,而不是“一個(gè)返回i自身的委托”。這一切,都是在“聲明”這段代碼在“做什么”,而不是“怎么做”。

沒(méi)錯(cuò),“類型推演”,“省略括號(hào)”和“省略return關(guān)鍵字”可能的確都是些“細(xì)小”的功能,但也正是這些細(xì)微之處帶來(lái)了編碼方式上的關(guān)鍵性改變。

使用示例二

使用Lambda表達(dá)式還可以節(jié)省許多代碼(相信您從第一個(gè)示例中也可以看出來(lái)了)。不過(guò)我認(rèn)為,最省代碼的部分更應(yīng)該可能是其“分組”和“字典轉(zhuǎn)化”等功能。因此,我們來(lái)看第二個(gè)示例。

這個(gè)示例可能更加貼近現(xiàn)實(shí)。不知您是否關(guān)注過(guò)某些書籍后面的“索引”,它其實(shí)就是“列出所有的關(guān)鍵字,根據(jù)其首字母進(jìn)行分組,并且要求對(duì)每組內(nèi)部的關(guān)鍵字進(jìn)行排序”。簡(jiǎn)單說(shuō)來(lái),我們需要的其實(shí)是這么一個(gè)方法:

static Dictionary<char, List<string>> GetIndex(IEnumerable<string> keywords) { ... }

想想看,您會(huì)怎么做?其實(shí)不難(作為示例,我們這里只關(guān)注小寫英文,也不關(guān)心重復(fù)關(guān)鍵字這種特殊情況):

static Dictionary<char, List<string>> GetIndex(IEnumerable<string> keywords){    // 定義字典    var result = new Dictionary<char, List<string>>();    // 填充字典    foreach (var kw in keywords)    {        var firstChar = kw[0];        List<string> groupKeywords;        if (!result.TryGetValue(firstChar, out groupKeywords))        {            groupKeywords = new List<string>();            result.Add(firstChar, groupKeywords);        }        groupKeywords.Add(kw);    }    // 為每個(gè)分組排序    foreach (var groupKeywords in result.Values)    {        groupKeywords.Sort();    }    return result;}

那么如果利用Lambda表達(dá)式及.NET框架中定義的擴(kuò)展方法,代碼又會(huì)變成什么樣呢?請(qǐng)看:

static Dictionary<char, List<string>> GetIndexByLambda(IEnumerable<string> keywords){    return keywords        .GroupBy(k => k[0]) // 按照首字母分組        .ToDictionary( // 構(gòu)造字典            g => g.Key, // 以每組的Key作為鍵            g => g.OrderBy(k => k).ToList()); // 對(duì)每組排序并生成列表}

光從代碼數(shù)量上來(lái)看,前者便是后者的好幾倍。而有關(guān)“聲明式”,“what”等可讀性方面的優(yōu)勢(shì)就不再重復(fù)了,個(gè)人認(rèn)為它比上一個(gè)例子給人的“震撼”有過(guò)之而無(wú)不及。

試想,如果我們把GetIndexByLambda方法中的Lambda表達(dá)式改成.NET 2.0中delegate形式的寫法:

static Dictionary<char, List<string>> GetIndexByDelegate(IEnumerable<string> keywords){    return keywords        .GroupBy(delegate(string k) { return k[0]; })        .ToDictionary(            delegate(IGrouping<char, string> g) { return g.Key; },            delegate(IGrouping<char, string> g)            {                return g.OrderBy(delegate(string s) { return s; }).ToList();            });}

您愿意編寫這樣的代碼嗎?

因此,Lambda表達(dá)式在這里還是起著決定性的作用。事實(shí)上正是因?yàn)橛辛薒ambda表達(dá)式,.NET中的一些函數(shù)式編程特性才被真正推廣開來(lái)。“語(yǔ)言特性”決定“編程方式”的確非常有道理。這一點(diǎn)上Java是一個(gè)很好的反例:從理論上說(shuō),Java也有“內(nèi)聯(lián)”的寫法,但是C#的使用快感在Java那邊還只能是個(gè)夢(mèng)。試想GetIndexByLambda在Java中會(huì)是什么情況3

public Dictionary<Char, List<String>> GetIndexInJava(Enumerable<String> keywords){    return keywords        .GroupBy(            new Func<String, Char> {                public Char execute(String s) { return s.charAt(0); }            })        .ToDictionary(            new Func<Grouping<Char, String>, Char> {                public Char execute(IGrouping<Char, String> g) { return g.getKey(); }            },            new Func<Grouping<Char, String>, List<string>> {                public List<String> execute(IGrouping<Char, String> g)                {                    return g                        .OrderBy(                            new Func<String, String> {                                public String execute(String s) { return s; }                            })                        .ToList();                }            });}

一股語(yǔ)法噪音的氣息撲面而來(lái),讓人無(wú)法抵擋。由于Java中的匿名類型語(yǔ)法(即上面這種內(nèi)聯(lián)寫法)連類型信息(new Func<String, Char>{ ... }這樣的代碼)都無(wú)法省去,因此給人非常繁瑣的感覺(jué)。面對(duì)這樣的代碼,您可能會(huì)有和我一樣的想法:“還不如最普通的寫法啊”。沒(méi)錯(cuò),這種函數(shù)式編程的風(fēng)格,由于缺乏語(yǔ)言特性支持,實(shí)在不適合在Java語(yǔ)言中使用。事實(shí)上,這種內(nèi)聯(lián)寫法很早就出現(xiàn)了(至少在02、03年我還在使用Java的時(shí)候就已經(jīng)有了),但是那么多年下來(lái)一點(diǎn)改進(jìn)都沒(méi)有。而Lambda表達(dá)式出現(xiàn)之后,社區(qū)中立即跟進(jìn)了大量項(xiàng)目,如MoqFluent NHibernate等等,充分運(yùn)用了C# 3.0的這一新特性。難道這還不夠說(shuō)明問(wèn)題嗎?

對(duì)了,再次推薦一下Scala語(yǔ)言,它的代碼可以寫的和C#一樣漂亮。我不是Java平臺(tái)的粉絲,更是Java語(yǔ)言的忠實(shí)反對(duì)者,但是我對(duì)Java平臺(tái)上的Scala語(yǔ)言和開源項(xiàng)目都抱有強(qiáng)烈的好感。

既然談到了函數(shù)式編程,那么就順便再多說(shuō)幾句。其實(shí)這兩個(gè)例子都有濃厚的函數(shù)式編程影子在里面,例如,對(duì)于函數(shù)試編程來(lái)說(shuō),Where常被叫做filter,Select常被叫做map。而.NET 3.5中定義的另一些方法在函數(shù)式編程里都有體現(xiàn)(如Aggregate相當(dāng)于fold)。如果您對(duì)這方面感興趣,可以關(guān)注Matthew Poswysocki提出的Functional C#類庫(kù)。

總結(jié)

既可以提高可讀性,又能夠減少代碼數(shù)量,我實(shí)在找不出任何理由拒絕Lambda表達(dá)式。

哦,對(duì)了,您可能會(huì)提到“性能”,這的確也是一個(gè)重要的方面,不過(guò)關(guān)于這個(gè)話題我們下次再談。受篇幅限制,原本計(jì)劃的“上”“下”兩篇這次又不得不拆開了。至于其他的內(nèi)容,也等討論完性能問(wèn)題之后再說(shuō)吧。

當(dāng)然,世界上沒(méi)有東西是完美的,如果您覺(jué)得Lambda表達(dá)式在某些時(shí)候會(huì)給您帶來(lái)“危害”,那么也不妨使用delegate代替Lambda表達(dá)式。例如,為了代碼清晰,在某些時(shí)候還是顯式地指明參數(shù)類型比較好。不過(guò)對(duì)我而言,在任何情況下我都會(huì)使用Lambda表達(dá)式——最多使用“(int a, string b) =>”的形式咯,我想總比“delegate(int a, string b)”要統(tǒng)一、省事一些吧。

相關(guān)文章

  • 從.NET中委托寫法的演變談開去(上):委托與匿名方法
  • 從.NET中委托寫法的演變談開去(中):Lambda表達(dá)式及其優(yōu)勢(shì)
  • 從.NET中委托寫法的演變談開去(下):性能相關(guān)

注1:嚴(yán)格說(shuō)來(lái),這里的body是一個(gè)“語(yǔ)句(statement)”,而不是“表達(dá)式(expression)”。因?yàn)橐粋€(gè)委托其實(shí)是一個(gè)方法,因此使用Lambda來(lái)表示一個(gè)委托,其中必然要包含“語(yǔ)句”。不過(guò)在目前的C#中,Lambda表達(dá)式還有一個(gè)作用是構(gòu)造一顆“表達(dá)式樹”,而目前的C#編譯器只能構(gòu)造“表達(dá)式樹”而不是“語(yǔ)句樹”。

注2:事實(shí)上,在.NET 2.0使用delegate關(guān)鍵字定義匿名方法時(shí)已經(jīng)可以有些許“類型推演”的意味了——雖然還是必須寫明參數(shù)的類型,但是我們已經(jīng)可以省略委托的類型了,不是嗎?

注3:除非我們補(bǔ)充Func、Enumerable,Dictionary,Grouping等類型及API,否則這段代碼在Java中是無(wú)法編譯通過(guò)的。事實(shí)上,這段Java代碼是我在記事本中寫出來(lái)的。不過(guò)這個(gè)形式完全正確。

NET技術(shù)從.NET中委托寫法的演變談開去(中):Lambda表達(dá)式及其優(yōu)勢(shì),轉(zhuǎn)載需保留來(lái)源!

鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請(qǐng)第一時(shí)間聯(lián)系我們修改或刪除,多謝。

主站蜘蛛池模板: 天天色综合社区 | 2021国产精品成人免费视频 | www.亚洲视频| 亚洲人成综合 | 福利毛片 | 久久精品爱 | 国产精品亚洲第一区二区三区 | 欧美成人三级伦在线观看 | 婷婷sese| 国产在线视频在线 | 欧美日韩一区二区亚洲 | 国产精品亚洲四区在线观看 | 激情五月开心婷婷 | 国产伦精品一区二区三区女 | 九九免费视频 | 美女黄网站免费观看 | 欧洲免费无线码二区5 | 韩国视频一区 | 亚洲首页国产精品丝袜 | 亚洲小说图片视频 | 欧洲在线观看在线视频吗 | 欧美成人网在线综合视频 | 国产精品一区二区三区四区五区 | 久久久久国产视频 | 国产精选第一页 | 国产69成人免费视频观看 | 91中文字幕在线观看 | 久久99国产精品久久99软件 | 亚洲天堂五月天 | 欧美成国产精品 | 国内在线观看精品免费视频 | 色综合网址| 国产色婷婷免费视频 | 激情总合网| 在线青草| 久久老色鬼天天综合网观看 | 日日噜噜噜噜人人爽亚洲精品 | 精品久久久久久久久久中文字幕 | 亚洲欧美日韩国产色另类 | 亚洲精品国产手机 | www视频在线播放 |