天天躁日日躁狠狠躁AV麻豆-天天躁人人躁人人躁狂躁-天天澡夜夜澡人人澡-天天影视香色欲综合网-国产成人女人在线视频观看-国产成人女人视频在线观看

我眼中的C# 3.0

緣起

  每次有新技術(shù)發(fā)布時(shí),我們總能感受到兩種截然不同的情緒:一種是恐懼和抵抗,伴隨著這種情緒的還有諸如"C# 2.0用的挺好的,為什么要在C# 3.0搞到那么復(fù)雜?"或者"我還在使用C#1.0呢?"等言辭;另一種則是興奮和擁抱,伴隨著這種情緒的還有諸如"原來(lái)這個(gè)問(wèn)題在C# 3.0里可以這么簡(jiǎn)單!"等言辭。

  最近我在公司內(nèi)部做一個(gè)LINQ的系列講座,在我為其中C#3.0新特性這一講準(zhǔn)備演示文稿時(shí),突然萌生了寫下這篇文章的念頭。語(yǔ)言的特性乃至其本身并沒(méi)有對(duì)錯(cuò)之分,是否接受在很大程度上是一個(gè)感性問(wèn)題,即你是否喜歡這樣的做事方式,我并沒(méi)有打算說(shuō)服任何人接受C# 3.0和LINQ,寫這篇文章也只是想和大家分享一下我自己的感受。

  有一次我觀看一個(gè)關(guān)于Expression Blend的培訓(xùn)視頻,里面說(shuō)了一句讓我印象非常深刻的話:

I know how it works because I know why it works.

  細(xì)細(xì)品味這句話,你會(huì)感受到它所要傳達(dá)的信息:理解為何需要這個(gè)功能可以幫助你更好地理解如何使用這個(gè)功能,而這也正是我要在這篇文章里采用的表達(dá)方式。

你是如何創(chuàng)建屬性的?

  如果你長(zhǎng)期使用C#,相信你不會(huì)對(duì)屬性這個(gè)東西感到陌生。一般地,屬性是對(duì)私有字段的一個(gè)簡(jiǎn)單包裝,就像這樣:

代碼 1

  使用屬性而不是直接公開私有字段的一個(gè)好處就是在屬性的獲取訪問(wèn)器或設(shè)置訪問(wèn)器里加入額外的邏輯并不會(huì)為客戶端代碼帶來(lái)麻煩,例如你想在設(shè)置標(biāo)題的時(shí)候做一些額外的檢查。但如果你只是簡(jiǎn)單地包裝一下,像上面的代碼那樣,就會(huì)發(fā)現(xiàn)你其實(shí)多寫了不少可以省略的代碼。既然Title屬性和m_Title私有字段對(duì)應(yīng),獲取訪問(wèn)器就肯定是返回m_Title的值,而設(shè)置訪問(wèn)器也肯定是把值設(shè)到m_Title。再者,如果你只通過(guò)Title屬性來(lái)訪問(wèn)這個(gè)數(shù)據(jù),那么m_Title私有字段就會(huì)變得無(wú)足輕重,這樣的話,為什么不交給編譯器代勞呢?這個(gè)時(shí)候,C# 3.0的自動(dòng)屬性就可以派上用場(chǎng)了:

代碼 2

  編譯器會(huì)為你創(chuàng)建一個(gè)私有字段,并讓獲取訪問(wèn)器和設(shè)置訪問(wèn)器指向這個(gè)私有字段。當(dāng)然,如果有需要,例如要在獲取訪問(wèn)器或設(shè)置訪問(wèn)器里加入額外的邏輯時(shí),你隨時(shí)可以對(duì)獲取訪問(wèn)器和設(shè)置訪問(wèn)器進(jìn)行展開。

你是如何初始化對(duì)象的?

  現(xiàn)在,假設(shè)我們有這樣一個(gè)類:

代碼 3

  你會(huì)怎樣初始化它?一種做法是用Book的默認(rèn)構(gòu)造函數(shù)創(chuàng)建對(duì)象實(shí)例,然后分別為每個(gè)屬性賦值:

代碼 4

  另一種做法是使用C# 3.0對(duì)象初始化器:

代碼 5

  乍看一下,C#3.0的做法似乎沒(méi)有讓人感到任何優(yōu)越感,現(xiàn)在,請(qǐng)你仔細(xì)觀察一下,這兩份代碼分別包含多少個(gè)";"?代碼4有5個(gè)";",意味著它用了5個(gè)語(yǔ)句進(jìn)行初始化;而代碼5只有1個(gè)";",意味著它只用了1個(gè)語(yǔ)句進(jìn)行初始化。從詞法的角度來(lái)看,如果此刻我只能接受一個(gè)表達(dá)式,那么代碼4的做法就幫不上忙了。一個(gè)變通的方法是為Book類提供帶參的構(gòu)造函數(shù),但這種方法也有弊端,用戶可能只想在初始化時(shí)為部分屬性提供數(shù)據(jù),而我們又無(wú)法確切預(yù)知用戶會(huì)提供哪些屬性的組合,于是,我們可能要為用戶提供足夠多的構(gòu)造函數(shù)重載,嗯,有點(diǎn)無(wú)聊,也有點(diǎn)多余。另一個(gè)變通的方法是提供接受最多參數(shù)的構(gòu)造函數(shù),如果用戶為某個(gè)參數(shù)傳遞null,那么就忽略與之對(duì)應(yīng)的屬性,這個(gè)方法比較接近代碼5的做法,不同的是,如果你的屬性很多,而用戶關(guān)心的只是很少一部分,就可能不得不輸入很多null了。

  現(xiàn)在,假設(shè)你要實(shí)例化一組Book對(duì)象,并把它們儲(chǔ)存在一個(gè)集合里,你會(huì)怎么做?下面是通常的做法:

代碼 6

  如果結(jié)合使用C# 3.0的對(duì)象初始化器和集合初始化器,你就可以把代碼簡(jiǎn)化為:

代碼 7

  集合里的每個(gè)元素通過(guò)","分割,結(jié)合對(duì)象初始化器使用,整個(gè)集合的結(jié)構(gòu)顯得比較明晰。字典的初始化也可以同樣簡(jiǎn)單:

代碼 8

  說(shuō)到這里,我相信你也能感覺(jué)到,C#似乎正在表達(dá)式化,以前需要很多條語(yǔ)句才能做到的事情,現(xiàn)在卻可以用單個(gè)表達(dá)式描述出來(lái),而這種理念也滲透在整個(gè)C# 3.0的氛圍里。

你是如何把運(yùn)算邏輯外包出去的?

  假設(shè)我現(xiàn)在得到了一組Book的實(shí)例對(duì)象,你要對(duì)它們進(jìn)行排序,那么你如何告訴它你要按價(jià)格來(lái)排序呢?

代碼 9

  在C# 1.0里,我們需要特意為它提供一個(gè)獨(dú)立的方法:

代碼 10

  然后向Sort()方法傳入所需委托的實(shí)例:

代碼 11

  這在C# 2.0里可以進(jìn)一步簡(jiǎn)化為:

代碼 12

  如果使用C# 2.0的匿名方法,我們可以省去很多不必要的代碼:

代碼 13

  此外,使用匿名方法,Sort()方法和你希望它用來(lái)比較兩個(gè)Book實(shí)例對(duì)象的邏輯可以放在同一個(gè)地方;而使用獨(dú)立的命名方法,包含這個(gè)邏輯的方法可能會(huì)由于整理代碼而被挪到別的地方。這樣,當(dāng)你看到代碼12時(shí),為了了解它內(nèi)部的實(shí)現(xiàn),就不得不花一些精力去尋找Compare()方法了。當(dāng)然,你可以爭(zhēng)辯說(shuō),我們可以制定一個(gè)編碼規(guī)范,使得Compare()方法必須緊貼在Sort()方法的下方。是的,你可以,但如果這個(gè)邏輯并不需要重用,那么使用匿名方法還是具有明顯的優(yōu)勢(shì)的。如果這個(gè)邏輯需要重用,那么匿名方法就無(wú)能為力了。

  現(xiàn)在,讓我們來(lái)考察一下代碼13,有沒(méi)有發(fā)現(xiàn)匿名方法的表達(dá)方式還不夠簡(jiǎn)練?我們知道,books集合里面只有Book的實(shí)例對(duì)象,所以Sort()方法傳給我們兩個(gè)參數(shù)的類型必定是Book,而Sort()方法期待的結(jié)果正是x.Price.CompareTo(y.Price)這個(gè)表達(dá)式的運(yùn)算結(jié)果,至于delegate和return這樣的字眼可以說(shuō)在這里完全是多余的,那么為什么我們不直接這樣表達(dá)呢:

代碼 14

  這就是C#3.0引入的Lambda表達(dá)式語(yǔ)法。我見(jiàn)過(guò)一些人,他們通常強(qiáng)調(diào)盡可能簡(jiǎn)單,但若事情突然變得比他們預(yù)期的還要簡(jiǎn)單很多,他們就開始感到不適,甚至拒絕接受這種簡(jiǎn)單,其實(shí)即使事物的發(fā)展方向和你的前進(jìn)方向相一致,但如果發(fā)展速度大大超越了你,仍然有可能引發(fā)你內(nèi)心對(duì)失控的恐懼。我希望Lambda表達(dá)式語(yǔ)法不會(huì)讓你感到太大的不適,當(dāng)然我更希望你會(huì)喜歡上它。

  Lambda表達(dá)式的理解其實(shí)可以很簡(jiǎn)單,就是"=>"左邊的參數(shù)參與右邊的表達(dá)式運(yùn)算,而運(yùn)算結(jié)果將會(huì)返回,這有點(diǎn)像化合反應(yīng),即兩種或兩種以上的物質(zhì)(左邊的參數(shù))生成一種新物質(zhì)(右邊的表達(dá)式的運(yùn)算結(jié)果),不同的是,Lambda可以不接收任何參數(shù),也可以不返回任何結(jié)果。

  "=>"右邊除了可以放表達(dá)式之外,還可以放語(yǔ)句,像這樣:

代碼 15

  我們把它稱為L(zhǎng)ambda語(yǔ)句(Lambda Statement),或許你已經(jīng)發(fā)現(xiàn),它和匿名方法相比只是不需要寫delegate關(guān)鍵字和參數(shù)類型。

你是如何為對(duì)象擴(kuò)展與之相關(guān)的功能的?

  我一直在想,為什么String類沒(méi)有提供一個(gè)Reverse()方法,把字符串翻轉(zhuǎn)呢?我猜可能是因?yàn)檫@種操作沒(méi)有什么現(xiàn)實(shí)意思,除非你要做一個(gè)文字游戲。實(shí)現(xiàn)Reverse()方法并不難,下面是其中一種做法:

代碼 16

  使用方法也非常簡(jiǎn)單:

代碼 17

  你甚至可以把Reverse()方法放到某個(gè)靜態(tài)類里,例如Utils,這樣,代碼17就可以變成:

代碼 18

  在C# 3.0之前,你最多只能走到這里,而到了C# 3.0,你還可以使用擴(kuò)展方法對(duì)它做進(jìn)一步調(diào)整,使代碼18變成:

代碼 19

  怎么樣,看上去就像Reverse()方法是屬于String的,而你所需要做的僅僅是在Reverse()方法的target參數(shù)前面加上"this"關(guān)鍵字:

代碼 20

  我們知道,計(jì)算機(jī)的底層世界并不知道什么是面向?qū)ο螅覀冊(cè)趯?duì)象里定義的實(shí)例方法都包含一個(gè)隱藏參數(shù),這個(gè)參數(shù)就是指向當(dāng)前對(duì)象實(shí)例的指針,C#3.0的擴(kuò)展方法在形式上模仿了這種做法,但由于擴(kuò)展方法本質(zhì)上并不屬于與之相關(guān)的類,所以你無(wú)法在擴(kuò)展方法里訪問(wèn)類內(nèi)部的私有成員。

  就上面的討論來(lái)說(shuō),你可能認(rèn)為,和代碼18相比,代碼19并沒(méi)有太大的優(yōu)勢(shì),那么為什么需要擴(kuò)展方法呢?假設(shè)我們手頭上有一堆書,我想找到最便宜的LINQ的書,使用標(biāo)準(zhǔn)查詢運(yùn)算符的話可以這樣寫:

代碼 21

  我們知道,Where()、OrderBy()和First()等都是擴(kuò)展方法,如果C# 3.0不支持?jǐn)U展方法,那么代碼21就不得不寫成這樣了:

代碼 22

  代碼21的可讀性明顯比代碼22的高,也顯得更自然,而此時(shí)我們只是使用了3個(gè)標(biāo)準(zhǔn)查詢運(yùn)算符,你可以想象一下,在沒(méi)有擴(kuò)展方法的支持下要表達(dá)更復(fù)雜的查詢會(huì)是怎樣一番情景?

你是如何表達(dá)你想要的東西的?

  現(xiàn)在,假設(shè)我想找到最便宜的LINQ的書,使用C# 2.0的語(yǔ)法,我可能需要這樣:

代碼 23

  雖然我已經(jīng)使用了Array.IndexOf()方法、List<T>.Sort()方法和匿名函數(shù)來(lái)簡(jiǎn)化代碼,但仍然無(wú)法掩蓋一個(gè)事實(shí),那就是我在講述如何獲取我想要的東西,而這也正是命令式編程(Imperative Programming)的核心思想。

  如果使用C# 3.0的語(yǔ)法,情況將會(huì)大不一樣:

代碼 24

  在這里,你表達(dá)了你想要的東西,而不是獲取這些東西的具體步驟,這是聲明式編程(DeclarativeProgramming)的核心思想,這樣做的好處是明顯的,你的需求可以被重新解析并執(zhí)行,必要時(shí)還可以對(duì)底層的實(shí)現(xiàn)進(jìn)行優(yōu)化,但由于你并不關(guān)心和牽扯到具體的實(shí)現(xiàn)上,所以那些優(yōu)化并不會(huì)導(dǎo)致你修改代碼。

  命令式編程就像過(guò)程管理,你深入執(zhí)行的細(xì)節(jié),繼而對(duì)整個(gè)過(guò)程的執(zhí)行實(shí)施控制;而聲明式編程則像目標(biāo)管理(MBO),你制定目標(biāo),并把任務(wù)分配下去執(zhí)行。代碼23給人的感覺(jué)就是整個(gè)執(zhí)行過(guò)程都非常的清楚,你可以對(duì)任何一個(gè)步驟進(jìn)行修改或者調(diào)優(yōu);而代碼24給人的感覺(jué)就是你除了說(shuō)出你想要什么,你什么也不能做,這對(duì)于那些過(guò)程管理?yè)泶髡邅?lái)說(shuō)可能是不可接受的,他們感到對(duì)事物失去了控制,無(wú)法建立安全感,因而產(chǎn)生了焦慮。曾經(jīng)有人向我抱怨:如果你使用了LINQ,你就只能迫使自己相信它的實(shí)現(xiàn)是很好的。想想看,如果你的公司把飯?zhí)脴I(yè)務(wù)承包給一個(gè)餐飲公司,你的公司可以插手別人如何招聘廚師、如何采購(gòu)食物、如何燒菜燒飯嗎?選擇LINQ意味著你愿意把執(zhí)行細(xì)節(jié)交給別人去處理,從而脫離這些細(xì)節(jié),如果你根本無(wú)法放下對(duì)這些細(xì)節(jié)的控制,那么LINQ可能并不適合你。

  很難說(shuō)這兩種編程方式孰優(yōu)孰劣,因?yàn)樵谀承﹫?chǎng)合下,善于過(guò)程管理的管理者確實(shí)更能讓事態(tài)朝正確的方向發(fā)展;而在另一些場(chǎng)合下,目標(biāo)管理為實(shí)現(xiàn)者提供足夠的自由度,更能激勵(lì)他們積極地進(jìn)行思考。管理界對(duì)于過(guò)程管理和目標(biāo)管理孰優(yōu)孰劣之爭(zhēng)論似乎從來(lái)沒(méi)有停過(guò),更何況編程界對(duì)于命令式編程和聲明式編程孰優(yōu)孰劣之爭(zhēng)論,我個(gè)人倒是更傾向于把這看成是找出更適合你自己的風(fēng)格,而不是盲目聽(tīng)信別人的說(shuō)法。語(yǔ)言到底是發(fā)揮積極作用還是消極作用在很大程度上是取決于使用者的,我們應(yīng)該使用語(yǔ)言有利的一面來(lái)協(xié)助我們的工作,而不是使用其有害的一面來(lái)傷害自己和別人。

  回到代碼24,它把滿足條件的書的所有信息都返回給我,如果我只需要書名和作者名字呢?我們知道,在面向?qū)ο蟮氖澜缋铮畔?chǔ)存在對(duì)象里,于是我們不得不走到一個(gè)尷尬的境地,那就是我們要為此創(chuàng)建一個(gè)臨時(shí)類:

代碼 25

  噩夢(mèng)正式開始了,如果我需要書名和價(jià)格呢?如果我需要書名、作者和價(jià)格呢?……(讀者可以自行補(bǔ)全這個(gè)列表)這個(gè)時(shí)候就輪到C# 3.0的匿名類型和隱式類型化變量出場(chǎng)了:

代碼 26

  因?yàn)槟涿愋褪怯删幾g器自動(dòng)生成的,而在你寫代碼的時(shí)候它還沒(méi)有名字,所以你無(wú)法用這個(gè)類型來(lái)聲明這個(gè)變量,此時(shí)"var"關(guān)鍵字就派上用場(chǎng)了。這個(gè)是"var"關(guān)鍵字的最初目的,但得益于類型推斷系統(tǒng),我們還可以使用"var"關(guān)鍵字聲明任何本地變量,只要我們?cè)诼暶鞯耐瑫r(shí)給予它初始化,否則編譯器無(wú)法進(jìn)行推斷。曾經(jīng)有人問(wèn)我:如果我想返回代碼26里的wanted7怎么辦?我們知道,方法的返回值需要明確給出類型,而在我們寫下代碼26時(shí),編譯器還沒(méi)有給查詢表達(dá)式里的匿名類型取名。如果你真的要把它返回,你只能把方法的返回值類型定為IEnumerable<object>,因?yàn)槲覀冎荒艽_定匿名類型是object的后代,但這樣一來(lái),客戶端代碼的日子就不太好過(guò)了,因?yàn)槌送ㄟ^(guò)反射來(lái)訪問(wèn)你的對(duì)象,它別無(wú)他選。如果你真的要把它返回,那就意味著你和客戶端代碼有共享這個(gè)對(duì)象的需求,此時(shí)恰當(dāng)?shù)淖龇☉?yīng)該是使用命名類型。另外,代碼26里構(gòu)建匿名類型時(shí)的"book.Title"是"Title =book.Title"的簡(jiǎn)寫,當(dāng)你省略"Title ="時(shí),編譯器會(huì)假定你希望匿名類型的這個(gè)屬性的名字和Book.Title的一樣。

  匿名類型還有一個(gè)有趣的地方,它曾經(jīng)是可變的(mutable),后來(lái)卻變成不可變的(immutable),Sree《Immutable is, the new Anonymous Type》一文中給出了這個(gè)轉(zhuǎn)變的解釋。我們知道,在面向?qū)ο蟮氖澜缋铮瑢?duì)象封裝并維護(hù)自身的狀態(tài),我們通過(guò)調(diào)用對(duì)象的方法所產(chǎn)生的副作用來(lái)影響對(duì)象的狀態(tài),而不可變則是函數(shù)式編程(Functional Programming)的核心特征,或許你已經(jīng)感受到了,C#3.0引入了大量函數(shù)式編程的東西,而函數(shù)式編程語(yǔ)言似乎也要風(fēng)生水起,這究竟意味著什么呢?

前路在何方?

  無(wú)論你是否承認(rèn),C# 3.0在表達(dá)上比它之前的版本要來(lái)的簡(jiǎn)單,但要獲得這種簡(jiǎn)單,你必須先用很多東西武裝自己的腦袋,這使我想起曾經(jīng)在一本書里看到的一句話:

簡(jiǎn)單是由復(fù)雜來(lái)支撐的。

  不同語(yǔ)言之間的相互滲透已經(jīng)不再是什么新奇之事了,引入其它語(yǔ)言的功能有時(shí)候甚至可以看作是在戰(zhàn)略上入侵對(duì)手的市場(chǎng),這在某種程度上有點(diǎn)像金融業(yè)的混業(yè)經(jīng)營(yíng)。下一個(gè)版本的C#將會(huì)是怎樣的呢?或許這個(gè)問(wèn)題令你興奮不已,你甚至希望現(xiàn)在就讓C#Team看看你的創(chuàng)造力;或許這個(gè)問(wèn)題令你痛心不已,你害怕自己無(wú)法適應(yīng)下一波的變革,因?yàn)樽兏锟赡軐?dǎo)致動(dòng)蕩,動(dòng)蕩可能帶來(lái)失控,失控可能引發(fā)焦慮。不管怎樣,該來(lái)的是無(wú)法回避的,或許現(xiàn)在先讓我們看看Matthew Podwysocki的《What Is the Future of C# Anyways?》是否有一些啟示……

NET技術(shù)我眼中的C# 3.0,轉(zhuǎn)載需保留來(lái)源!

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

主站蜘蛛池模板: 人妻少妇久久久久久97人妻 | 菠萝菠萝蜜免费播放高清 | 高h原耽肉汁动漫视频 | 欧美成人性色生活18黑人 | 天天日免费观看视频一1 | 中文字幕久精品视频在线观看 | A片毛片免费视频在线看 | 久久777国产线看观看精品卜 | 男女牲交大战免费播放 | 国产精品亚洲国产三区 | 白白操在线视频 | 55夜色66夜亚洲精品播放 | 久久精品亚洲精品国产欧美 | 办公室沙发口爆12P 办公室日本肉丝OL在线 | 亚洲国产成人99精品激情在线 | 啪啪做羞羞事小黄文 | 香蕉久久av一区二区三区 | 日夜啪啪一区二区三区 | 亚洲综合久久一本伊伊区 | 息与子在线交尾中文字幕 | 无码AV精品一区二区三区 | 乱子伦在线观看中文字幕 | 国产免费人成在线视频有码 | 最近2019年日本中文免费字幕 | 日日做夜夜欢狠狠免费软件 | 四虎影5151毛片在线看 | 草699一码二码三码四码 | 国产色无码精品视频国产 | 日韩欧美成人免费中文字幕 | 国产精品久久精品 | 青青草狠狠干 | 97在线观看成人免费视频 | 11 13加污女qq看他下面 | 中文字幕精品无码一区二区 | 国产原创中文视频 | 国产精品免费一区二区区 | 福利片福利一区二区三区 | 大香交伊人 | 精品国产免费第一区二区 | 亚洲AV成人无码999WWW | 亚洲伊人久久综合影院2021 |