爲何要用 C# 來做爲您的首選編程語言

    由於您能夠用,而且也是您的最佳選擇!之因此可用,是由於 C# 可以很好地在 Mac、Linux、Android 和 iOS 上運行(對了,還有 Windows);它能夠在您最喜好的編輯器上運行;它在一個穩定的企業級平臺上通過了充分的時間驗證;最爲重要的是:它是徹底開源的!之因此是您的最佳選擇,是由於 C# 是編程語言創新方面的領導者,是原生跨平臺移動應用程序的最佳選擇,而且還有不少的優勢超乎您的想象。在本次 GOTO Copenhagen 2016 大會講演上,Mads Torgersen 邀請您一塊兒來探索 C# 的核心,探究爲何它仍然散發着活力,並探尋將來 C# 的發展趨勢。java

概述   c++

    我是 Mads Torgersen,就任於微軟的 C# 部門。我如今年紀大了,所以我隨身都穿着這件 T恤,上面印着我正在負責的項目和語言名稱,以防我忘掉它們。這裏我想談一談 C#,爲何我要推薦用它來做爲你們的首選編程語言呢(即便到目前爲止您尚未接觸過 C#)。程序員


Stack Overflow - 最受歡迎和喜好的技術
編程

    Stack Overflow 每一年都會進行一次調查,詢問不少開發者們都關心的問題(固然,在不少方面這些問題都是很具備傾向性,是很不科學的)。您必須在 Stack Overflow 上才能參與。
數組

    C# 是一門被普遍使用的編程語言(排行第四,排行前三當中有一門實際上並不屬於編程語言——我說的不是 JavaScript,我說的是 SQL)。能夠看出,C# 是一門主流語言。
安全

    他們一樣還問開發者們:是否還想繼續使用目前正在用的語言,並讓人們投票出他們最喜好的技術。C# 一樣也在這個列表當中。這說明人們都很喜歡 C# 這門語言。此外還有其餘人們也喜歡的語言,可是您還能夠注意到,這些語言中的大部門要麼就是受衆較少,要麼就是很是專業化,不少都是某種狂熱信仰的一部分了。在這兩個列表當中,只有少數纔是用途普遍、受人們高度喜好的。很高興能看到 C# 位於這個列表的三大最受歡迎的技術之一,其中兩個是編程語言,而且* Python 也在這裏面*。
服務器

    咱們不斷思考咱們的所做所爲,怎樣纔多是正確的呢,怎樣才能讓咱們在多年之後仍然喜歡它。彷佛並非全部人都用過 C#,由於不少人所在的公司已經有 10 年多的歷史了,裏面存在了不少的遺留代碼。目前 C# 仍然保持着活力,咱們但願它能將這份活力保持下去。咱們一樣也有各式各樣的想法,而這驅動了 C# 的演進。
數據結構

    咱們很是渴望去演進 C#。若是您看過現代語言的演變進程的話(從少到多),就會明白咱們積極保持語言現代化的目的所在了。做爲參與編程語言演變的一份子,咱們有些時候是推進者,有些時候是跟隨者,不管如何,咱們都試圖讓 C# 成爲程序員們現在的絕佳選擇之一。咱們不該該搞所謂的「限定」,只侷限於某幾個平臺,由於過去十年當中就有人這麼作了,結果可想而知。
架構

    我還想提一提 F#,由於這至關因而咱們的姊妹語言,它很是受歡迎,由於它很輕巧、也很強大。F# 是一門功能強大的語言,咱們在與 F# 團隊的合做當中獲益良多,而且它也給咱們提供了不少設計靈感。
併發

    (說明:F#是由微軟發展的爲微軟.NET語言提供運行環境的程序設計語言。它是基於Ocaml的,而Ocaml是基於ML函數程序設計語言的。函數編程是解決許多棘手問題的最好方法,可是,純函數編程並不適合常規編程。所以,函數編程語言逐漸吸取了命令式、面向對象的編程模式,不只保持了函數編程範式,同時也混合了其餘須要的功能,使函數編程編寫各類類型的程序都很容易。F# 就是這種嘗試的成功表明,比其餘函數編程語言作得更多。F#主要是爲了解決特定的某些複雜問題因此自己定位使得VS沒有提供F#的ASP.NET/WPF/GDI+的模板,若要使用須要本身配置。因此,通常狀況下都是用C#。

 

時代在改變 - 爲何要選擇 C#

    在愈來愈多的場景當中,您均可以使用 C#來進行編程。咱們正在努力地作出一種改變。C# 在 Windows 當中是一種很重要的主要編程語言,但同時,咱們在其餘平臺上仍然還很是稚嫩。至少大多數平臺是這樣。如今 C# 已是全部平臺上可選的編程語言之一,這很是鼓舞人心,然而咱們同時也有些頑固,此外這些平臺上也出現了各式各樣新穎的語言。這使得咱們迫切地但願其餘平臺上也可以使用咱們的語言。

    咱們已經不少次對咱們的語言進行了演進。實現 C# 底層的編譯器和 IDE 技術(名爲 Roslyn 項目)爲 C# 的編程啓用了獨一無二的場景。其中一個好處是,咱們將 C# 的核心從 Windows 和 Visual Studio 當中剝離了出來,這意味着 C# 可以很容易地在其餘 IDE 當中使用。您能夠用本身喜好的 IDE 或者編輯器來編寫 C# 代碼。

    咱們已經將 C# 從徹底的專有技術轉變爲了徹底開放源代碼的技術了。這意味着每一個人都能給 C# 貢獻代碼了,固然也已經不少人蔘與到這個項目當中來了。咱們如今正在同社區展開交流,如今 C# 的演進很是迅速。由於如今這更像是一個協同項目了,而不是「微軟說怎樣就怎樣了」。這很是讓人興奮。如今語言的變革已經再也不是三年才一代了。「這是咱們努力的成果,但願您能喜歡它」,咱們如今天天都在與社區討論將來的方向問題。咱們隨時隨地都可以在網上、Github 上獲得反饋。所以,咱們語言設計的質量也愈來愈高。

    讓咱們從 C# 的各個項目開始一一介紹。


無處不 C# - Xamarin

    Xamarin 之前是一家獨立的公司。咱們六個月以前收購了他們。這是一種使用 C# 來構建跨平臺應用的技術,用來製做原生的 Android 和 iOS 應用。它可讓您使用相同的語言、相同的源代碼來構建絕大多數應用組件,從而可以爲多種不一樣的移動平臺編寫應用。

    它適用於 iOS 和 Android,一樣也能夠用在 Mac 之上,順便提一下,Windows 也可使用。它能夠建立高品質的原生 UI。有許多大型應用正在使用這門技術,由於它能夠極大地減小單獨在這些平臺上編碼的工做量。它也容許您使用與後臺相同的語言,例如說 Java,不過 Swift 和 Objective-C 還未支持。它以支持的平臺量取勝,是一門實現應用的絕佳語言。

    它基於 Mono 項目,這是多年之前從微軟離職的員工所實現的開源項目,而且一直維護,致力於可以在其餘平臺上也可以使用 C#。雖然在微軟當中的咱們有些固步自封,可是他們卻在咱們以前看到了 C# 跨平臺的潛力,並實現了這個偉大的跨平臺項目。Xamarin 正是基於此而生的,您在 iOS 和 Android 應用商店當中看到的許多應用都是基於 C# 的,要麼是使用 Xamarin,要麼是使用 Unity,這應該是業界領先的遊戲引擎。


無處不 C# - Unity

    Unity 也是一個基於 Mono 的項目,它的 2D、3D 遊戲引擎是用 C# 來編寫的。有不少遊戲是用 C# 編寫的。


無處不 C# - .NET 核心

    在微軟,咱們正努力完善 .NET 核心,這是對整個 .NET 技術棧、運行時以及代碼庫等內容的全新實現,旨在保證輕量、而且可供服務端使用,而且可用做雲和服務器工做負載。它是跨平臺的,適用於 Linux、Mac 和 Windows。咱們在此之上還放置了 ASP.NET 框架,這是一個被普遍使用的 Web 框架,目前您就能夠在非 Windows 的機器上運行 ASP.NET 了,而且它還徹底開源了!爲何咱們要創建一個單獨的核心呢?這將有助於您可以構建更輕量的服務器。

    首先,咱們移除了 UI 框架,可是 UI 框架可以獨立部署。例如,您能夠將運行時環境同代碼一併發佈;雲端無需安裝各式各樣的依賴文件。它擁有一個更優秀的架構,更適合微服務的部署。它一樣也致力於使咱們的服務端平臺更加現代化。這些不一樣的 .NET 運行在不一樣的平臺上,若是沒有統一的部署,那麼隨着版本的擴散,一切就會變得很是的混亂,尤爲是您做爲第三方庫提供方的時候。您須要某種東西可以跨平臺運行,以便解決您的問題。

    咱們一樣也實現了一個名爲 「.NET 標準庫」的東西,咱們提供了一個全部 .NET 平臺全兼容的 API。若是您須要使用它的話,您能夠直接在工具庫當中連接這個 .NET 標準庫便可,它將能夠在全部平臺上運行。您也能夠收回在 .NET 生態系統中隨處可用代碼的能力。咱們將隨時對標準庫進行升級,包括引入新的核心基本庫,這樣您任意連接所須要的標準庫便可。所以 C# 可以在不少地方運行,但願這可以說服你們來嘗試 C#,由於這比之前有着更大的適用範圍。可以實現這個着實讓人興奮。


Roslyn - C# 語言引擎

    我想要多談論一些底層的內容,也就是談一談 Roslyn 項目。

    咱們對 C# 引擎進行了現代化。之前只有一個簡單的 C# 編譯器。它使用 C++ 編寫的,以後咱們有了 Visual Studio,而後將 C# 集成了進去。然而這二者使用 C# 的方式都不一樣,沒有任何的代碼共享,所以也沒有辦法知道對方的相關信息。若是有人想要使用 C# 相關內容的話,那就須要爲之編寫一套工具,或者爲它們本身的 IDE 來添加支持,這是一個很大的工做量。由於人們不得不重頭開始編寫,由於 C# 引擎對它們而言是個黑盒。這對咱們來講也並非讓人滿意的。

    所以咱們決定,是時候重寫編譯器了,咱們不只重寫了 C#,而且還採用了一些新的架構。用來實現 C# 語義的工具只能有一個。咱們須要構建一個你們均可以使用的工具,以便知足他們想經過 C# 實現某些功能的願望。這個工具與平臺無關,不管是什麼樣的平臺,好比說批處理過程、編譯器,仍是某些相似 IDE 的交互式環境,均可以使用這個工具進行。這是一個很難的目標,它花費了咱們大量的時間來實現,而且也投入了大量的人力。可是如今,咱們推出了 Roslyn API,它切實知足了咱們所設定的目標。

    你們都須要知道,絕大多數 C# 工具已完成 Roslyn 引擎的遷移,不過仍然有一些沒有。場下還有一位演講者 Ted Brangs 的項目就是例外,由於他是出於某些技術緣由的考慮。咱們的想法是,這裏是您須要用於實現 IDE 和編輯器的代碼庫。若是您須要使用不一樣類型的分析工具,那麼能夠導入它們來對代碼中的問題進行分析。若是您想要對代碼進行操做的話,例如代碼遷移、代碼補全或者重構之類的操做,那麼您也可使用它。若是您須要生成源代碼,那麼您也可使用它。若是您須要實現更多交互式場景,例如腳本或者 REPL(好比說如今咱們在 Visual Studio 當中包含了一個 C# REPL,它是在 Roslyn 的基礎上構建的),那麼這個引擎仍然可以編譯代碼並完成輸出。

    這裏是您所可以實現的一個範例,也就是人們可以用 Roslyn 作些什麼。這可能會致使編程語言相關的工具呈現爆炸式增加,由於人們如今能夠更快地來對 C# 進行處理了。它可以與 C# 很好地協同工做。如今您已經有不少能夠輕易獲得的信息了,好比說語法、語義等內容,您就能夠按照本身的想法,向所須要的地方添加特定的位碼了。有一個社區項目充分利用了這點,那就是 OmniSharp。

    請記住:C#語言引擎--Roslyn


OmniSharp - 隨時隨地可編輯 C#

    OmniSharp 是一個特別的項目,旨在讓 C# 可以在您喜好的編輯器上運行。他們實現這個功能的方法很聰明:因爲 C# 如今已經能夠在任何地方運行了,所以他們採用 Roslyn C# 引擎,而後在一個單獨的進程中運行,無論您在進行開發的機器是什麼(例如一臺 Mac)。他們使用一個單獨的進程來運行引擎,而後剩下所須要作的就是添加一個很簡單的集成層,而後加入到特定編輯器中的集成層當中,這樣雙方即可以經過進程來進行通訊,從而讓編輯器瞭解到 C# 的所有信息。

    每次您按下了一個鍵,例如輸入了點語法,而後這個時候您須要顯示代碼補全,這個時候它就會詢問旁邊的進程:「用戶輸入了點語法,那麼我須要在代碼補全列表當中顯示什麼呢?」。Roslyn 知曉全部的語義,它隨後會告訴編輯器:「這五種方法是可用的,顯示它們便可」。

    經過這種分離方式,使得全部的語義分析過程被包裹在單獨的進程當中進行,而後經過標準的數據傳輸方式來進行數據的交換。這使得咱們能夠在不少的編輯器當中去實現表現良好的 C#,而且還能夠提供語義感知功能(固然,這種作法褒貶不一)。

    我須要再提一點,對於微軟的 Visual Studio 而言,咱們使用 OmniSharp 來實現其 C# 模式,由於這是一個擴展,能夠在任何地方加載。它不會內置在編輯器當中。C# 就像其餘語言同樣,對 Visual Studio 而言就是一個擴展組件。

    (我的認爲:OmniSharp項目對Roslyn引擎進行了封裝,以更方便的使第三方編輯器調用,好比本身開發一個C#代碼編輯器

 

演示 - Roslyn 分析器

    讓咱們舉一個更具體的例子。爲了幫助人們將重點放在語言實現以外的地方,咱們建立了這個框架,名爲 Analyzer,經過它能夠輕鬆地對人們的源代碼進行分析、診斷,最後輸出結果。這樣,咱們即可以提供代碼修正建議了。

    若是您須要:   

    一、您的組織有須要強制執行的代碼格式
    二、常常執行重構
    三、想要與你們分享代碼
    四、須要讓代碼修正自動化

    那麼這個工具正是您所須要的。

    這個項目您能夠在 Visual Studio 當中安裝,而後就能夠開始使用了。當您打開某個項目的時候,它就已經自帶了爲這個項目而創建的樣板代碼。具體而言,當您的項目像這樣進入調試模式的時候,而後分析器便會提取您的代碼,而後進行代碼修正,最後將結果輸出。分析器能夠以批處理代碼的方式運行,也能夠將其做爲 Nuget 包進行分發。它是以 Visual Studio 擴展程序的身份出現的,在 Visual Studio 的完整版本當中,它是做爲調試模式的一部分運行的。如今我運行了 Visual Studio,而後它便開始執行代碼修正了。這個是我在完整版本當中的 Visual Studio 所編寫的操做。

    如今讓咱們在這個完整版本的 Visual Studio 中打開一些代碼。我尚未徹底實現完這些分析器的功能。這裏是一些咱們想要進行操做的示例代碼。出於簡便起見,我想要實現的東西是語法分析,這裏我能夠定義各類各樣的語義規則。Roslyn 引擎提供了完整的信息供我使用,我能夠定義 if 或者 else 語句當中沒有柯里化(curlies)語句是非法的代碼樣式。

    咱們須要實現那種老式的、固化的代碼風格,也就是始終須要添加柯里化的語句,由於接下來編輯代碼的時候,並不會獲得太多的 BUG。咱們須要在某些狀況下阻止這種代碼的出現,出於時間考慮,我這裏只對 if 進行實現,咱們固然也能夠將其應用到 else 規則當中來。這裏讓咱們來實現一個小型的代碼分析器。

    這裏我不會中止使用這個完整版本。我須要放置一個斷點。每當咱們看到 if 語句的時候,最開始須要作的就是要註冊它,咱們須要調用這個方法 AnalyzeNode。每當 Visual Studio 中的源代碼分析器命中了一個 if 語句,那麼就會自動前往這裏,而後我就能夠執行一些操做了,隨後它就會繼續分析代碼,直到命中下一個斷點。如今我可以獲得這段代碼當中的所有信息了,接下來我就能夠添加操做了。我獲得的東西是一個 context 對象。

    讓咱們看一下這個 context 當中的內容。若是這個 if 語句不符合要求,我就能夠報告一個診斷過程。我能夠獲得當前語法樹的相關節點,一般就是這個 if 語句當中的內容。讓咱們開始處理吧,咱們將鼠標懸停在這個上面,因爲咱們位於調試模式,所以這裏能夠看到一個實際傳入的對象。咱們能夠看到這裏的確就是一個 if 語句。讓咱們使用 Roslyn 構建的對象模型,將其轉換爲 if 語句模型。

    這裏我可以獲得一個語法樹節點,它剛好是 IfStatementSyntax 的派生類。咱們能夠聲明 var ifStatement 賦值爲該值。如今這就是咱們以後惟一所須要調用的對象了,我這裏將再也不檢查它是不是一個 if 語句。若是這個語句內部的東西不呈現柯里化的話,那麼這個東西將被稱爲 Block,這就說明這段代碼是不符合規範的。若是它不屬於 SyntaxKind.Block 的話,接下來我就會提示錯誤了。我會告訴用戶:「這裏不對」。如今我須要彙報診斷結果。不過如今我尚未實現,我須要進行一點重構操做,來爲之生成一個局部變量。

    我能夠經過 Diagnostic.Create 建立一個診斷,它須要我提供一些參數。首先是一個被稱爲 Rule 的描述符,而後我須要指定 location。也就是當問題出現的時候,我須要在代碼當中顯示波浪線。而後我須要指明當前違背了何種規則。而後指明不符合規則的位置。讓咱們再對這段實現進行一下重構,生成一個局部變量。這就是我在調試模式所有所須要作的。

    那麼什麼是所謂的「位置」呢?也就是我當前正在處理的這個節點:if 語句。那麼咱們須要在那裏放置提示信息呢?讓咱們放在這個 if 關鍵字上吧。if 語句這裏擁有一個 if 關鍵字,由於這是一個具體的語法樹。它擁有裏面代碼的所有實現細節,包括全部的位置。讓咱們從中取得相關的位置。這裏經過 GetLocation 方法來獲取。咱們獲得 if 關鍵字的位置,而後將這個位置傳入到這個方法當中。我編寫了一些代碼。讓咱們移除這個斷點,而後繼續在調試器當中運行。咱們等待一下子,看看發生了什麼,好的,如今您會看到 if 語句當中出現了波浪線。

    這就是個人所有操做:編寫三四行代碼就能夠識別出問題所在,並告訴框架在哪裏顯示這個問題。爲了向您證實它可以起做用,我把這段代碼註釋掉,您會發現如今波浪線消失了。

    當您試圖實現更爲複雜的操做的時候,就變得有點困難了,可是這仍然是一個相對簡單的語言模型,由於它包含了完整的語法和綁定語義,人們能夠用它來構建工具,而後與別人分享,這樣便可以讓每一個人在編輯 C# 的過程中遵循一致的原則,無論它們所用的編輯器是什麼,只要是基於 Roslyn 的就行。不管人們位於哪一個平臺,他們都可以從中收益匪淺。

    我寫的這個分析器一樣能夠在批處理模式當中運行。它能夠成爲編譯過程中的一部分,能夠標記警告或者錯誤,就如同編譯器所作的那樣。我還能夠實現修復工具(但我以後的時間不打算演示這個功能),它能夠基於咱們制定的規則對代碼進行修復。
    這就是 Roslyn 這邊的演示,它是如何幫助人們得到更好的編碼體驗的,一個更好的 C# 開發體驗。它給予了咱們更好的代碼庫,更好的架構,顯然在 C# 當中,咱們能夠對它進行 Dogfood 測試,以更好地幫助咱們演進語言自己。


C# 的演進

    現在咱們對 C# 進行演進較之前已經容易不少了,而且咱們可讓社區經過貢獻代碼而參與到 C# 的演進當中來。然而,這些演進的版本(我不會把全部版本都列述一遍)——展示了咱們在創新過程中的破壞性。

    我想以 Async 爲例。咱們編寫了 LINQ,也就是咱們在 C# 版本 3 的時候引入的查詢操做。咱們採起了一個頗有趣的冷門語言所存在的概念,並嘗試將其主流化,將這些概念引入到 C# 這個主流語言當中,從而幫助它們開擴更廣闊的市場。C# 當中的 LINQ 查詢就是您可以在其餘函數式編程語言當中找到的一個例子。

    咱們把它們與 Lambda 表達式結合在一塊兒。如今世界上絕大多數語言均可以使用 Lambda 表達式。固然幾年之前這並不常見。現在 Java 也可使用 Lambda 了。固然,咱們仍是回到 C# 版本 2 來,在這個版本中咱們引入了泛型,僅僅只是慢了 Java 一拍,可是泛型已經成爲了 C# 必不可少的一部分。由於在 C# 當中,泛型是深嵌入運行時當中的。Java 則是採起了比較謹慎的方法,讓泛型直接編譯成所須要的東西。

    當您將泛型機制實如今運行時當中的時候,這對獲取語義而言是 100% 有好處的,然而這一樣也意味着性能特性將大相徑庭,尤爲是當語言中存在值類型的時候,當 C# 從版本 1 升級的時候就遇到這個問題,Java 可能會在將來採起這種方式來實現泛型。當語言當中存在值類型的時候,您但願泛型可以識別出它們,而且專門爲這些值類型設定特定的規則,也就是在使用泛型的時候就無需對其進行封裝和分配內容空間。這樣泛型就可以讓代碼更快,而不是產生拖累。

    自咱們升級以後,泛型便成爲了不少語言的標配特性。因爲泛型的引進,咱們藉此便可以正確地實現查詢功能。因爲泛型是深嵌入在運行時當中實現的,所以咱們可使用反射機制,這樣咱們即可以實現一些奇怪的代碼引用,而後將 C# 代碼轉換爲 SQL。這都是基於類型能夠變換的機理才得以實現,而且甚至在運行時都是能夠如此使用。

    咱們將動態值集成到了靜態類型的系統當中,這種類型的類型是動態的、變化的,咱們稱之爲 Dynamic。再次強調,其底層實現仍然是基於泛型來實現的,這使得其運行效率高效,而且避免了不少無謂的封裝操做。Async 則是深度依賴於泛型機制。在 C# 版本 6 當中,咱們引入了 Roslyn,所以對於實現任何特定的語言特性而言,再也不是一件難事。咱們如今擁有了更多的靈活性,如今咱們即可以實現那些還未實現的微小特性了,咱們經過實現這些特性,可使得開發工做更簡單、更優雅、更簡潔、更輕鬆。

    以後咱們在 C# 版本 6 當中引入了 swath ,也就是如今的 C# 版本。而後在以後的 C# 版本 7 當中,咱們將再次引入一些更重要、更底層的特性,咱們想要從函數式編程語言當中進行大量借用特性,我認爲咱們下一步多是須要引入相關的特性,來處理那些沒必要用面向對象的方式處理的數據。你們都知道咱們一開始是一門很是純粹的面嚮對象語言,而後只是傾向於添加一些函數式的特性來做爲面向對象的一些擴充,而且咱們也在嘗試將這二者很好地結合起來。這個靈感來自於 Scala,而且 JVM 也在這麼作,嘗試將函數式和麪向對象結合起來,可是咱們絕對不會拋棄面向對象這個根本。

    Scala是一門多範式的編程語言,一種相似java的編程語言,設計初衷是實現可伸縮的語言,並集成面向對象編程和函數式編程的各類特性。


演示:Async

    我打算跳過這個 Async 的演示,由於大多數人可能都知道它的做用是什麼了。那麼讓咱們來談一談即將到來的 C# 版本 7。

    C# 1.0 with Visual Studio.NET
       C# 2.0 with Visual Studio 2005
       C# 3.0 with Visual Studio 2008,2010(.net 2.0, 3.0, 3.5)
       C# 4.0 with Visual Studio 2010(.net 4)
       C# 5.0 with Visual Studio 2012(.net 4.5),2013(.net 4.5.1, 4.5.2)
       C# 6.0 with Visual Studio 2015(.net 4.6, 4.6.1, 4.6.2)
       C# 7.0 with Visual Studio 2017(.net 4.6.2, 4.7)
       C#版本是VS版本決定的,也可與相應的.net framework版本對應起來


C# 版本 7

    讓咱們從元組 (tuple) 開始。首先這裏我有一個完整的程序。而後裏面有一些數字值。由於數字是很是容易理解的,可是可能不是全部人都可以知曉其中的含義:咱們如今擁有的是二進制的字面量。這是一個很微小的特性。當您教孩子編程的時候,這就很是有用了,這些位於數字下方的則是位 (bit)。我如今要再添加一個。咱們這裏一樣還有數字分隔符,就像其餘語言所作的那樣,您能夠將這些分隔符放在須要的地方,從而讓數字更容易閱讀。

    我這裏打算實現一個名爲 Tally(計數器) 的方法,它將數組當中的數字累加起來,得出計算結果。這樣咱們即可以對這些數字調用這個 Tally 方法。固然目前這個方法我尚未實現,讓咱們使用重構來生成這個方法。這是一個靜態方法。目前它的返回值爲 Void。或許它應該返回其餘的東西。我以爲它應該要返回這個累加值,或者直接返回數字的總數?我以爲二者都是很是重要的。可是如今在 C# 當中您只能返回同樣東西,可是在不久的未來,您就能夠返回兩個返回值了,甚至三個、四個、更多。只要您想,您實際上能夠建立一個超大的元組,可是這多是個糟糕的主意。

    如今讓咱們返回兩個 int 值。這是一個元組類型。它表示裏面有兩個 int 類型,這應該很容易理解。這裏是元組字面量,咱們仍是先返回一些虛擬的值。這裏麪包含了一些所需的值,經過括號和逗號包裹起來,固然這個語法應該比較正常。當我使用這個方法的時候,我能夠獲取它的返回值,而後我會發現我獲得了一個元組類型。

    那麼咱們該如何使用元組呢?讓咱們將其輸出出來。插入字符串。總和可能位於這裏的第一個位置。讓咱們來看一下元組有些什麼:Item1 和 Item2。很顯然,咱們知道它們分別表明了什麼,咱們能夠直接使用這兩個名稱。雖然這個名字比較糟糕,可是能用就行。C# 當中的元組還能夠爲不一樣的元素賦予不一樣的名稱。這裏我將指定各個元素的名稱。這是什麼意思呢?

    當我獲取到這個元組的時候,它即可以告訴我它裏面的元素是什麼。這一樣也意味着我來到這裏,輸入點語法,就能夠看到這些預覽而已;最終的版本應該須要隱藏掉這些糟糕的名字。這裏咱們會看到擁有了很顯然的名字,由於這裏您能夠看到 sum,咱們能夠直接使用它來獲取元組當中對應的值。以前那些是底層當中的真實名稱,可是編譯器知道跟蹤這些別名,並使用它們來替代顯示。

    元組當中擁有元素別名是很是重要的,由於您極可能記不住這個元組是姓在前仍是名在前。所以元組須要提供這些信息,這樣纔可以讓人容易理解。您須要有獲取別名的能力。

    固然,您極可能會但願在獲取到元組的時候,就馬上將其解構,將元組當中的值分開,固然您也能夠在 C# 當中作到這一點。您能夠在這裏聲明 sum, count,而後元組便會馬上解構爲 sum 變量和 count 變量。這樣咱們即可以再也不使用 t. 前綴,而是直接使用 sum 和 count。

    讓咱們如今來實現這個方法。這裏咱們再也不返回一個虛擬值,讓咱們返回一個真實的結果。這裏咱們對這些數字執行 foreach 操做;這裏咱們將其稱爲 values。接下來咱們在每次遍歷的時候更新結果值。

    result 這個名字太長了,我想將其命名爲 r。讓咱們聲明 r = 這個新的元組字面量。這裏我但願可以獲取舊有的值。我但願 r 一樣也有元素別名。讓咱們前去給其增長別名。您能夠在元組字面量當中爲其賦別名。這裏的語法和命名參數所作的相同。如今 r 擁有了 s 和 c 兩個元素別名。咱們能夠調用 r.s,即可以在這裏獲取到以前的總和值,而後加上新的值 v,而後這裏的 r.c 總數須要加 1。

    您可能會在想,這不是很複雜麼,很浪費空間。這不是每次都直接分配一個新的數組?或者說在每次遍歷的時候偶會建立一個新的元組麼?這樣作的話,在資源受限的設備或者須要花錢的雲端當中是否是很很差?爲這些元組分配內存空間是否是很是浪費?

    這並不會致使空間的浪費,由於元組不屬於對象。元組被實現爲值類型,是以 C# 當中的結構體實現的。所以它們不會分配內存空間。它們只是直接更新某些在堆上的東西。這些值類型是使用 copy 來傳遞的,而不是經過引用來傳遞。元組沒有標識,它們當中僅僅只是包含值而已(我以爲元組應該是這樣的)。它們應該只是短暫的存在。所以它們不該該擁有生命週期,這樣才能更有效率。

    元組不只是值類型,它們一樣也是可變類型。我知道在函數式陣營當中的人們會很反對這種作法,可是我仍是堅持元組是可變類型。您能夠修改元組裏面的值。而不是這樣子寫:r.s += value。做爲一個單獨的語句 r.c++ 就很好了,就不用更多的重複了。此外我還能夠交換元組當中元素的位置,這並非危險的操做,由於在線程之間沒有共享的可變狀態,由於這是一個結構體。沒有對象去共享它。您能夠隨意將其傳遞到任何地方,它是用拷貝操做執行的,不存在危險的狀況。

    爲何咱們總要強調面向對象呢?爲何一切都必需要封裝起來呢?元組沒有屬性,僅僅只是字段。它們是包含某些可變公共字段的結構體。獲取很是簡單,您能夠很輕鬆地明白本身的作法。這就是元組的正確用法,由於它們不是抽象類型;它們不會封裝任何東西——它們僅僅只是值而已。

    關於元組的其餘幾件事是:因爲元組是一種類型,所以它能夠判斷相等。例如,您能夠將元組做爲字典當中的鍵來使用。若是您想要讓兩個類型都做爲某個字典的鍵,那麼使用元組是再好不過的,這樣一切都相安無事。哈希值和其餘所用的東西在數據結構當中都可以正常的工做。

    固然,這也是從異步方法當中獲取多個值的絕佳方法,由於若是操做的是異步的話,您能夠返回 Task 的元組,這樣當全部的操做結束以前,您就能夠依次等待。獲得元組以後,就能夠對其解構並繼續前進。元組是很好的傳輸工具。對於 async 方法以及其餘方法而言,若是有多個返回值的話是很是糟糕的,由於您沒辦法輸出多個參數,可是如今經過元組您能夠實現這個操做了!

    (若是須要使用同一類型的多個對象,可使用集合和數組;若是須要使用不一樣類型的多個對象,可使用元組(Tuple)類型。.NET Framework定義了8個泛型Tuple類和一個靜態Tuple類,它們用做元組的工廠。元組用靜態Tuple類的靜態Create()方法建立。Create()方法的泛型參數定義了要實例化的元組類型。


將來展望:更多的模式

    咱們開始向 C# 當中添加模式匹配 (pattern matching)。

1 if (o is Point(5, var y)) { WriteLine($"Y: {y}"); } // 遞歸模式
2 
3 state = match (state, request) // 匹配表達式,匹配元組
4 { 5     (Closed, Open) => Opened, 6     (Closed, Lock) => Locked, 7     (Opened, Close) => Closed, 8     (Locked, Unlock) => Closed, 9 };

    這裏咱們從函數式陣營當中引入了一個全新的理念,咱們正在逐步實現這個功能。您會在將來的 C# 版本當中見到更多的內容,可是如今讓咱們跳過這裏,介紹一下第一種模式。

    讓咱們把這個例子變成包含遞歸數字列表的狀況。這裏咱們用的不是 int 數組,而是一個對象數組,其中咱們有一個約定,其內部的東西是 int 值或者是其餘包含 int 的數組,也能夠是新的對象數組,其中有一些 int 值嵌套在當中。或許若是裏面也能夠包含 null 可能會讓人能更加明白,如今咱們須要更新一下咱們的這個 Tally 方法,讓其可以處理這個數組。

    首先讓咱們直接替換爲這個對象數組,好的如今咱們獲得了一個錯誤,由於這個 v 再也不是 int 類型了;他是一個對象。咱們須要知道它是不是 int 值,若是是咱們就添加它。所以咱們須要進行一些邏輯處理;在過去,咱們會執行一個類型檢測。若是 v 是 int 類型的話,而後咱們就執行轉換並處理;可是,即使咱們檢測出它是 int 類型,這裏咱們實際上仍然不知道它是什麼。咱們必須再次執行檢查才能將其放入。

    相反在這裏,您能夠將其認爲是對 is 表達式的一個擴展。您如今能夠聲明一個變量。當您詢問它是否 is int 的時候,若是是,那麼就會取這個 int 值並將其賦到這個新的變量 i 當中。接下來變量 i 就有 v 的值了,可是類型已經肯定爲 int 了。如今咱們就能夠在這裏將其添加進去,運轉良好。

    is 表達式如今擴展爲容許模式的存在了,這是 C# 當中引入的一個新概念。模式,而不是類型。模式能夠是不少複雜的組合。如今模式還不能很複雜。基本上只可以容許常量或者類型模式。例如,能夠設定常量值 v is 7(如今這個被容許了,由於這屬於常量模式)。咱們正在實現更多的模式,將它們集成到語言特性當中來,好比說表達式。

    另外一個咱們正在集成的地方是,咱們正在嘗試將其整合到 switch 語句當中。我如今能夠對任何東西進行 switch,而原來 swtich 只能夠對原子類型進行操做。這是很古老的特性了,不過如今它能夠對任何東西進行操做了。咱們能夠對對象進行操做:switch on v。在個人這個 switch 語句當中,我能夠列舉沒有任何常量存在的狀況,如今這個屬於一種特殊的模式,不過能夠對任何模式進行操做。我能夠這麼聲明 case int i。(我必須記得要 break,這就是爲何在這裏我獲得了一個波浪線)。

    我這裏已經用了一種模式。我擴展了 switch 語句當中了 case 塊,以便其可以應用某種模式,而且能夠「當這種模式適用時,就執行此 case 塊」。我能夠對 swtich 語句進行現代化。我可讓對象數組成爲 case 的條件,這也是我所期待的另外一件事。讓咱們將其聲明爲 a,我能夠將條件放到 case 裏面。我能夠設定「我只須要長度大於 0 的對象 a,由於 a.Length 大於 0(不然就沒必要執行其餘操做了)」。在這種狀況下,我能夠設定 var t = Tally,而後加入嵌套數組,並將結果添加到 r;r = r。您知道後面的用法:r.s + t.sum、r.c + t.c。而後 break 退出。這是對既有模式特徵的一種泛化,也就是 C# 當中模式匹配所擁有的程度。

    在將來,咱們但願可以加入更多的模式。咱們須要更智能的模式。您應該須要可以使用遞歸模式。咱們讓您可以指定一個給定的類型,讓其可以被解構。例如,您能夠在 Point 類型進行指定,這樣它就能夠被解構,就像咱們以前對元組進行解構,分解爲不一樣的變量裏面。當類型被設定爲可解構的以後,咱們就將其與模式匹配結合在一塊兒,並容許您可以:同時檢查 o 是不是 Point 類型,若是是的話就對其進行解構,而且能夠應用遞歸模式:若是 o 是一個 Point,那麼這個點的第一個部分 x is 5,而後將第二個部分放到變量 y 當中。您能夠獲得更智能的模式。您也可使用它來構造不可讀的代碼,可是通常而言,若是可以更深刻模式,那麼您就會發現模式是很是有用的。

    咱們應該須要在新的地方當中添加模式。switch 語句是 20 世紀 60 年代的產物了。或許咱們能夠新增一個 switch 語句的表達式版本。或許是一個匹配表達式,而這是函數式語言當中所稱呼的,它具備更新的語法,基於表達式,而後 case 語句中也能夠添加表達式,這樣可讓代碼更爲簡潔。可是如今咱們已經有了模式的概念,咱們能夠添加新的模式,而後向新的地方添加新的模式。這就是咱們下一個版本的 C# 所須要關注的一件事,咱們已經在努力實現它們,由於 C# 版本 7 已經差很少完成了。(咱們尚未發佈,也不要問我何時發佈)。

    此部分關於C#新版本模式的概念理解起來比較模糊,仍是待之後版本發佈後實際使用一下體現會更貼切。


將來展望:可空的引用類型

1 string? n; // 可空的引用類型
2 string  s; // 不可空的引用類型
3 n = null;  // 容許;它是可空的
4 s = null;  // 警告!不該該爲空
5 s = n;     // 警告!類型不一樣
6 WriteLine(s.Length); // 容許;是不可空的
7 WriteLine(n.Length); // 警告!它可能爲空
8 if (n != null) { WriteLine(n.Length); } // 容許;您進行了類型檢查
9 WriteLine(n!.Length); // 若是存在的話固然能夠

    新的語言當中,有一個特性正在成爲主流,那就是類型系統可以進行區分的能力,也就是判斷類型是否可空。

    變量有時候可能會爲 null,由於它是值域的一部分;可是有些時候我並不但願空值出現,那麼爲何我須要隨時對引用錯誤進行處理呢?Swift 也有這項功能。咱們可否讓 C# 也實現這些功能呢,即使咱們如今已經推出了 7 個版本,而可空性徹底是一個基於運行時的玩意兒呢?

    咱們認爲能夠:咱們已經在 C# 當中爲可空值類型留下了尾隨問號標誌。若是咱們容許您將它應用於引用類型,或許這就是您陳述某個類型爲空的方式。另外一方面,若是您不這麼聲明的話,就說明您指望那裏的東西不可能爲空。

    咱們將幫助您進行維護,這意味着我能夠將 null 分配給 n,可是不可以分配給 s,而且若是沒有任何限定條件的話我也沒法將 n 賦值給 s,由於 n 的值極可能是 null。我保護變量以防止它持有不應持有的值。另外一方面,當我想要使用這個引用的時候,我能夠無需任何限定就執行 s.Length,由於咱們知道它可能不會爲 null。咱們沒法讓像 C# 之類的語言作一個保證,保證這裏必定有值。

    n.Length 會警告您它的值多是 null,您可能會獲得 null 引用異常。解決的方法是,這些新語言當中存有一種新的空值檢查特性。它們有一種新的模式匹配方法(或者某種能夠用來檢測 null 的東西)。咱們不打算改變您檢查 null 的方式。在 C# 當中,已經有 7 種檢查空值的方式存在了。相反,咱們想讓編譯器對其進行跟着,來檢查某個類型是否爲空。

    若是您有一個 if 語句,來判斷 n 不爲空的時候才進入到其中,那麼咱們就知道這個範圍已經通過了類型檢查了。事實上它的值不可能爲 null,咱們將會假設您使用點語法訪問內部成員是沒有任何問題的。那麼有沒有別的辦法處理它呢?固然有,然而您如今仍然還須要使用這種方式。您必需要隨時使用這種方法才能消除全部的空值引用異常。

    此外還有一個「強制」操做符,您能夠給某個可空值上面加一個前置感嘆號 (!),這就意味着您強制讓其不能爲空。您知道在這裏,它的值永遠不可能爲空,只要你能足夠勇敢、足夠確定,那麼您就可使用點語法,也就沒有警告的產生。咱們已經在開發這個功能,咱們但願可以在下一代 C# 當中獲得這個功能。

   但願這個功能是很是有用的。關於這個特性的一件趣事是:它不只須要深刻到語言內部進行調整,還須要確保咱們全部的框架都應用上這個特性,這樣您纔可以確保本身的代碼應用上了正確的可空值。這是一個很是具備挑戰性的功能,我以爲這是很是值得的。

   (我的看法:微軟在下一個C#版本中增長此定義, 目的仍是爲了代碼的安全性。如今的引用類型在定義時並無如此分開定義聲明,之後在定義如string s這種定義時,可潛移默化的表示是不能夠爲null的,這樣一方面能夠不用進行if(s==null)這樣的判斷;另外一方面也同時保證了在忘記進行此類判斷時,程序也不會「拋出未將對象引用到定義」等此類的異常。反之,若想定義能夠爲null的引用類型,則能夠以string? s的形式示人,算是微軟在語法方面更加規範化了。

    備註:本人基於對原文的理解,增長了我的備註(紫色斜體括號部分),如有錯誤,請讀者提出意見和見解,願和你們一塊兒進步!

   原文連接 by Mads Torgersen on Dec 27 2016

相關文章
相關標籤/搜索