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

我眼中的C# 3.0

緣起

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

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

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

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

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

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

  如果你長期使用C#,相信你不會對屬性這個東西感到陌生。一般地,屬性是對私有字段的一個簡單包裝,就像這樣:

代碼 1

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

代碼 2

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

你是如何初始化對象的?

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

代碼 3

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

代碼 4

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

代碼 5

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

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

代碼 6

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

代碼 7

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

代碼 8

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

你是如何把運算邏輯外包出去的?

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

代碼 9

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

代碼 10

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

代碼 11

  這在C# 2.0里可以進一步簡化為:

代碼 12

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

代碼 13

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

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

代碼 14

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

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

  "=>"右邊除了可以放表達式之外,還可以放語句,像這樣:

代碼 15

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

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

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

代碼 16

  使用方法也非常簡單:

代碼 17

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

代碼 18

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

代碼 19

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

代碼 20

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

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

代碼 21

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

代碼 22

  代碼21的可讀性明顯比代碼22的高,也顯得更自然,而此時我們只是使用了3個標準查詢運算符,你可以想象一下,在沒有擴展方法的支持下要表達更復雜的查詢會是怎樣一番情景?

你是如何表達你想要的東西的?

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

代碼 23

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

  如果使用C# 3.0的語法,情況將會大不一樣:

代碼 24

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

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

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

  回到代碼24,它把滿足條件的書的所有信息都返回給我,如果我只需要書名和作者名字呢?我們知道,在面向?qū)ο蟮氖澜缋铮畔Υ嬖趯ο罄铮谑俏覀儾坏貌蛔叩揭粋€尷尬的境地,那就是我們要為此創(chuàng)建一個臨時類:

代碼 25

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

代碼 26

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

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

前路在何方?

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

簡單是由復雜來支撐的。

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

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

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

主站蜘蛛池模板: 四虎最新永久免费视频 | 色老板在线视频一区二区 | 国产真实伦视频在线观看 | 一级做性色a爱片久久片 | 人人视频在线观看 | 日韩精品中文字幕久久 | 久久综合久久鬼 | 亚洲成人中文 | 成人看的午夜免费毛片 | 日本精品一区二区三区在线视频一 | 亚洲国产午夜 | 91精品综合久久久久m3u8 | 视频国产免费 | 欧美福利网站 | 久久涩视频 | 狠狠色噜噜狠狠狠狠97不卡 | 免费观看成人鲁鲁鲁鲁鲁视频 | 国产区在线免费观看 | 成人精品人成网站 | 色天天躁夜夜躁天干天干 | 国产福利写真视频在线观看 | 成人在线第一页 | 欧美人与物videos另类3d | 亚洲狠狠97婷婷综合久久久久 | 色播在线永久免费视频 | 亚洲精品香蕉婷婷在线观看 | 日本妇人成熟免费 | 精品视频免费在线观看 | 四虎4hu新地址入口 四虎4hu亚洲精品 | 国产成人精品免费视频大 | 91国内揄拍国内精品对白 | 好色123| 特黄aaaaa日本大片免费看 | 日产欧产va高清 | 亚洲成a人片毛片在线 | 午夜视频免费在线播放 | 成人小视频免费在线观看 | 精新精新国产自在现拍 | fefe66毛片免费看 | 欧洲美女高清一级毛片 | 在线视频综合网 |