第 66 講:C# 新語(yǔ)法的基本介紹
我們使用了 65 講把 C# 的基本使用方式和語(yǔ)法給大家介紹了一下。下面我們要說(shuō)的是,現(xiàn)代 C# 為了增進(jìn)語(yǔ)法的使用,簡(jiǎn)化代碼的書(shū)寫,避免程序員誤用一些語(yǔ)法,C# 創(chuàng)建出了新的一些語(yǔ)法格式的東西。從本講開(kāi)始,我們會(huì)為大家介紹一下 C# 的新語(yǔ)法的使用以及基本原理。
不過(guò)在這之前,我要先說(shuō)一下 C# 的運(yùn)行時(shí),還有 .NET、.NET Standard 和 .NET Framework 和 .NET Core 的相關(guān)概念,以及新語(yǔ)法可用性。
Part 1 框架介紹
所有的 C# 程序在運(yùn)行的時(shí)候都會(huì)預(yù)先檢查一個(gè)以 .NET 開(kāi)頭的東西。這個(gè)東西是一個(gè)框架,它包含了你運(yùn)行 C# 程序的相關(guān)內(nèi)容,比如編譯器啊、運(yùn)行時(shí)啊什么的。
.NET 讀作 Dot net。中國(guó)這邊一般讀作“點(diǎn) Net”,我更建議你讀 Dot net。
如果沒(méi)有這個(gè)所謂的 .NET 框架的話,系統(tǒng)會(huì)叫你先裝這個(gè)東西,程序才可以繼續(xù)運(yùn)行。
C# 目前有很多運(yùn)行框架,而 .NET 自己也分了不同的姊妹出來(lái)。其中常見(jiàn)的可運(yùn)行 C# 的程序的框架有這樣幾個(gè):
.NET Framework
.NET Core
.NET Standard
.NET
Mono
其中,開(kāi)頭是 .NET 的這四個(gè),是微軟自己搞出來(lái)的,而 Mono 則是第三方的框架。我們這里說(shuō)的是微軟官方給的框架,而 Mono 是第三方出的框架,雖然也很厲害,但畢竟微軟出的 C# 編程語(yǔ)言,而框架 .NET 系列也都是微軟的,所以這些框架會(huì)比較好講解一些,因此 Mono 不在本教程的討論范圍。
下面我們來(lái)說(shuō)一下這四個(gè)不同的都以 .NET 開(kāi)頭的框架,分別都是什么東西。
實(shí)際上,它們的差距并不大,要說(shuō)真的差別,大概就是一些支持的 API 可能不同,使用范疇可能不同,之類的。
.NET Framework 框架:一直用起來(lái)都比較方便,在 Windows 系統(tǒng)上非常好用。但現(xiàn)在出到 4.8 版本后就再也沒(méi)有更進(jìn)了,然后它被歸并到了別的框架里,而且不支持跨平臺(tái);
.NET Core 框架:微軟自己推出的、作為 .NET Framework 的替代品框架。這個(gè)框架可以跨平臺(tái),也支持更多的 API,只不過(guò)只到 3.1 版本就沒(méi)有了,后來(lái)和 .NET Framework 框架一樣,歸并到了別的框架里;
.NET Standard 框架:嚴(yán)格來(lái)說(shuō)它應(yīng)該更像是一個(gè)執(zhí)行標(biāo)準(zhǔn)。它約束了前面這兩種框架的 API 的包含涉獵范圍,比如 .NET Standard 的第幾個(gè)版本必須包含哪些 API 之類的。該框架只到 2.2 就沒(méi)有了,后來(lái)有替代品,所以這個(gè)框架就沒(méi)有更進(jìn)了;
.NET 框架:這就是我剛才說(shuō)的那些框架的替代品。這個(gè)框架的第一個(gè)版本就直接編號(hào)為 5,因?yàn)樗鳛?.NET Framework 和 .NET Core 的替代品出現(xiàn),API 也是這倆框架的歸并,而版本號(hào)總要比 .NET Framework 最后一版的版本號(hào) 4.8 要更大吧,所以就直接從 5 開(kāi)始編號(hào)。到現(xiàn)在還在不斷更進(jìn),也是跨平臺(tái)框架。
跨平臺(tái)(Cross Platform)是指程序可以在不同廠商的操作系統(tǒng)上運(yùn)行。比如說(shuō)大眾熟知的 iOS、Android、微軟 Windows 操作系統(tǒng)、蘋果 Mac Catalyst 操作系統(tǒng)等。
下面來(lái)說(shuō)一下,框架和 C# 語(yǔ)言版本為什么有關(guān)聯(lián),以及關(guān)聯(lián)關(guān)系。
為什么 .NET 框架取名的時(shí)候,居然是以一個(gè)完全不是數(shù)字或者字母的特殊符號(hào)作為起頭?我個(gè)人認(rèn)為,.NET 和“互聯(lián)網(wǎng)+”的概念類似,“互聯(lián)網(wǎng)+”它只是一個(gè)前綴,它的后面可以追加一些可以追加的東西拼湊為一個(gè)整體,比如“互聯(lián)網(wǎng)+教育”、“互聯(lián)網(wǎng)+金融”等。.NET 也是類似的道理,.NET 作為一個(gè)東西的后綴出現(xiàn),你可以在它的左邊追加上你想要的東西,讓 .NET 能夠做到實(shí)現(xiàn)到這些。比如 ML.NET(基于 .NET 平臺(tái)的機(jī)器學(xué)習(xí))、Math.NET(基于 .NET 平臺(tái)的數(shù)學(xué)運(yùn)算 API)等等。而 .NET 的“NET”其實(shí)可以不大寫的,因?yàn)樗?network 這個(gè)單詞的縮寫(前三個(gè)字母)。network 是網(wǎng)絡(luò)的意思,而微軟是想創(chuàng)建一個(gè)新的框架來(lái)互聯(lián)全球,但是直接叫 network 的話可能有點(diǎn)奇怪,所以就用了前三個(gè)字母,畢竟在瀏覽器里輸入的網(wǎng)址后綴里,除了 .com、.org 以外,也確實(shí)存在 .net 的情況,因此干脆就叫它 .NET 了。只不過(guò)因?yàn)樗且粋€(gè)框架名,所以在使用的時(shí)候需要全部的字母都大寫。
Part 2 .NET 相關(guān)框架的 C# 語(yǔ)言版本關(guān)系
既然我們已經(jīng)說(shuō)完了 .NET 的基本框架,那么我們來(lái)說(shuō)一下,它怎么就和 C# 扯上關(guān)系了。
.NET 這些框架的創(chuàng)建,自然是用來(lái)跑程序用的。而基于這個(gè) .NET 相關(guān)平臺(tái)跑出來(lái)的程序,我們稱為基于 .NET 的程序,或者直接簡(jiǎn)稱 .NET 程序。.NET 程序歸結(jié)到底,肯定是一個(gè)程序,那么它自然會(huì)需要編程語(yǔ)言的編譯過(guò)程才會(huì)產(chǎn)生一個(gè)程序。
C# 則是 .NET 框架里最知名的其中一種編程語(yǔ)言,它是微軟團(tuán)隊(duì)認(rèn)定的、用于 .NET 框架的最合適的編程語(yǔ)言。而除了 C# 外,以前的 Visual Basic 也在 .NET 框架里可以使用。不過(guò),Visual Basic 早期就有,而且語(yǔ)法在 .NET 框架里得到了提升和變動(dòng),因此大多數(shù)地方也直接把 .NET 平臺(tái)專用的 Visual Basic 稱為 Visual Basic.NET,以區(qū)分和避免和早期的 Visual Basic 重名;而 C# 一直是在 .NET 上使用的編程語(yǔ)言,因此雖然也有人說(shuō) C#.NET,但實(shí)際上它也就是我這里說(shuō)的 C#。大概是這么一個(gè)關(guān)系。
那么下面來(lái)說(shuō)一下,為什么框架自身會(huì)有版本更進(jìn)一說(shuō)??蚣茏约簩?shí)際上也是一個(gè)程序,它運(yùn)行和調(diào)度基于操作系統(tǒng),自己可以獨(dú)立運(yùn)行。但問(wèn)題就在于,框架本身的搭建和代碼編寫也會(huì)有很多復(fù)雜的地方,所以導(dǎo)致了 bug 會(huì)潛藏在里面。因此不斷更進(jìn) API、不斷更新修復(fù) bug 是不得不做的事情。
更新修復(fù) bug 是理所應(yīng)當(dāng)?shù)?,但為什么非要更進(jìn) API 呢?因?yàn)榉奖闶褂寐铩,F(xiàn)在的 .NET 框架微軟毫不客氣地直接把程序的源代碼給暴露給大家了,大家甚至可以自己下載下來(lái)使用。如果會(huì)深入做一些底層操作的程序員可能就會(huì)使用和幫助微軟團(tuán)隊(duì)更進(jìn)代碼修復(fù) bug。這個(gè)期間,程序員就會(huì)自己寫上一些新的數(shù)據(jù)類型到框架里面去,這些就幻化為了 API。API 的兩個(gè)作用:
程序員自己寫出來(lái)的,自己用,簡(jiǎn)化一些復(fù)雜的調(diào)用,方便代碼使用;
程序員自己寫出來(lái)的,但給別的看到了這些代碼的用戶用,用戶搭建程序的時(shí)候也會(huì)更方便。
所以,API 更進(jìn)是有意義的。
那么,C# 語(yǔ)言自己因?yàn)樗彩沁\(yùn)行于 .NET 框架之上,所以也會(huì)配合框架更進(jìn)對(duì)應(yīng)的功能??梢粋€(gè)編程語(yǔ)言哪里來(lái)的新功能呢?那就是它的語(yǔ)法了。就像是英語(yǔ)語(yǔ)法,我們也會(huì)誕生新的語(yǔ)法(比如 who 和 whom 在現(xiàn)代英語(yǔ)里都可以用于名詞性從句的引導(dǎo)詞,表示缺失的賓語(yǔ)成分)。C# 也是,以前很多地方都非常麻煩,比如所謂的屬性的模式:
我都知道我要封裝 _value
字段,但又要大量增加額外的代碼,顯然是不方便的。因此 C# 會(huì)對(duì)于這些地方進(jìn)行語(yǔ)法層面的更進(jìn),讓我們更好地、更快捷地書(shū)寫代碼。
那么,既然 C# 語(yǔ)法也有更新,而 .NET 的框架也有更新,那么自然就會(huì)存在一種對(duì)應(yīng)關(guān)系,比如我什么版本的框架用什么版本的編程語(yǔ)言。下面我們來(lái)給大家列舉這個(gè)表格。
注意這個(gè)表格很可能在以后有更新,但因?yàn)榻坛淌撬赖?,所以不可能全部列舉。這里只給出到我寫教程結(jié)束的時(shí)候(2021 年 10 月 10 日),框架都有哪些,版本都是什么,什么對(duì)應(yīng)關(guān)系。

接著說(shuō)一下 C# 1.1 的異步編程模型為什么我沒(méi)有講。這個(gè)我們本應(yīng)該放在多線程里講的,但是我們?cè)谥v解教程的時(shí)候(2021 年),早已發(fā)布了完全替代這個(gè)模型的新模式:基于任務(wù)的異步編程模型(Task-based Asynchronous Pattern,簡(jiǎn)稱 TAP)的相關(guān) API,可以完全替代原來(lái)的這個(gè)異步編程模型 APM。APM 全稱 Asynchronous Programming Model,翻譯出來(lái)直接就叫“異步編程模型”。因?yàn)?APM 的模型較為復(fù)雜,后來(lái)發(fā)布了第二種可以替代 APM 的模型叫做基于事件的異步編程模型(Event-based Asynchronous Pattern,簡(jiǎn)稱 EAP)。但這個(gè)也已經(jīng)廢棄,現(xiàn)在的 TAP 是第三代的異步編程的模式了,而且一直沿用到現(xiàn)在。所以我們以后遇到 TAP 的內(nèi)容(例如 Task
類型)我會(huì)對(duì) TAP 做一個(gè)深入的講解,而早期的 APM 和 EAP 我就不再講解了。
Part 3 如何在你的項(xiàng)目里使用新版的 C# 語(yǔ)法
3-1 修改辦法
這個(gè)確實(shí)有必要說(shuō)一下。說(shuō)了之后,以后我們?cè)谏?jí)新版本的語(yǔ)法的時(shí)候,我們將會(huì)套用這里的這個(gè)步驟來(lái)修改,因此我們這里詳細(xì)說(shuō)明一下修改步驟。
首先我們打開(kāi)一個(gè)我們之前寫的解決方案,找到我們的這個(gè)項(xiàng)目。

找到它的圖標(biāo)是這個(gè)綠色方框,里面寫的是“C#”字樣的這一項(xiàng)。直接雙擊它,你將會(huì)打開(kāi)這個(gè)項(xiàng)目的配置文件,文件的后綴名是 .csproj
。

這個(gè)文件是使用 XML 格式的語(yǔ)法來(lái)書(shū)寫的。如果你不知道 XML 是什么的話,可以不管它,聽(tīng)我說(shuō)就可以了。
找到 <PropertyGroup>
和 </PropertyGroup>
這一對(duì)(比如圖里是第 3 行和第 7 行),里面的部分里會(huì)有一個(gè)寫成 <LangVersion>數(shù)值</LangVersion>
別的東西先別動(dòng)。這樣你的項(xiàng)目就可以使用當(dāng)前版本(7.3)以及以前版本的所有 C# 語(yǔ)法了。
LangVersion
元素專門就是控制當(dāng)前項(xiàng)目是什么語(yǔ)言版本。而 LangVersion
是可有可無(wú)的。如果沒(méi)有的話,它默認(rèn)的情況是直接支持這個(gè)框架自己目前能支持到的語(yǔ)言版本。換句話說(shuō)就是查表,看剛才我列的那個(gè)表格。這個(gè)框架版本對(duì)應(yīng)語(yǔ)言版本是多少,那么我這個(gè) LangVersion
元素默認(rèn)就是多少,直接可以不寫出來(lái)都行。
3-2 TargetFramework
元素
介紹完 LangVersion
元素后,我們來(lái)說(shuō)一下它上面的這個(gè) TargetFramework
元素。這個(gè)可能不像是 LangVersion
一樣可有可無(wú),它總是顯式給出,表示當(dāng)前這個(gè)程序用的是什么版本的框架。剛才我們說(shuō)到,.NET 有四種不同的框架類別,對(duì)應(yīng)的書(shū)寫格式是這樣的:
.NET Framework:記作
net數(shù)值
,其中“數(shù)值
”就是寫版本號(hào),不過(guò)小數(shù)點(diǎn)全部省略。比如 .NET Framework 4.7.2 框架寫法是net472
;.NET Core:記作
netcoreapp數(shù)值
,其中“數(shù)值
”也是寫版本號(hào),但不省略小數(shù)點(diǎn)。比如 .NET Core 3.1 框架寫法是netcoreapp3.1
;.NET Standard:記作
netstandard數(shù)值
,其中“數(shù)值
”也是寫版本號(hào),但不省略小數(shù)點(diǎn)。比如 .NET Standard 2.0 框架寫法是netstandard2.0
;.NET:記作
net數(shù)值
,其中“數(shù)值
”也是寫版本號(hào),但不省略小數(shù)點(diǎn)。比如 .NET 5 框架寫法是net5.0
。
我們可以參照這個(gè)格式,然后看看你的電腦安裝了什么版本的 .NET 框架,然后就把該框架寫上去。不過(guò)按道理來(lái)說(shuō),你在創(chuàng)建項(xiàng)目和解決方案的時(shí)候,會(huì)讓你選擇,因此這一項(xiàng)很少自己去配置和修改它,除非你安裝了新框架之后想要手動(dòng)修改。
還有一些別的數(shù)值可以填入進(jìn)去,只不過(guò)這些會(huì)涉及到跨平臺(tái),而教程只涉及 C# 語(yǔ)法內(nèi)容,因此這里不展開(kāi)說(shuō)明。想了解可以查看 MSDN 官方文檔的相關(guān)內(nèi)容。
https://docs.microsoft.com/en-us/dotnet/standard/frameworks
Part 4 TargetFramework
和 LangVersion
可否不匹配
如題所示,你填入的 TargetFramework
和 LangVersion
的項(xiàng)目可否不按照剛才的表格那樣一一匹配呢?答案是可以的。不匹配是有兩種情況的,第一種是你填入的語(yǔ)言版本比 TargetFramework
的框架支持的版本要低,第二種則是你填入的語(yǔ)言版本比 TargetFramework
的框架支持的版本要高。下面我們說(shuō)一下后果。
4-1 LangVersion
填值超過(guò)框架支持版本
這種情況按理說(shuō)是不允許的,但其實(shí)解決方案仍然允許我們這么做。新版的語(yǔ)法大概就只有兩種類型:
語(yǔ)法上更新了,但它等于是一句或多句代碼的簡(jiǎn)化寫法(語(yǔ)法糖);
從底層更新了,語(yǔ)法本身和框架的底層 API 綁定起來(lái)才能起效。
所有的新語(yǔ)法不外乎就這兩類情況。如果屬于后者的話,這個(gè)語(yǔ)法可能你就無(wú)法使用,因?yàn)楫?dāng)前框架沒(méi)有這樣的 API,導(dǎo)致你無(wú)法使用這個(gè)語(yǔ)法來(lái)運(yùn)行操作程序;但如果屬于前者的話,那么編譯器自動(dòng)會(huì)被翻譯成低階的語(yǔ)法,但這些語(yǔ)法顯然底層就支持也不依賴額外的 API,所以它跟框架版本本身沒(méi)有特殊的關(guān)聯(lián)。
說(shuō)這個(gè)干什么呢?我是在告訴你,如果你這么去修改配置文件的話,雖然是 Visual Studio 自己是允許你這么做的,但是部分新語(yǔ)法屬于第二種情況,因而導(dǎo)致你無(wú)法正常使用它。那么具體是哪些新語(yǔ)法呢?這個(gè)得我們講到這些語(yǔ)法的時(shí)候你才會(huì)知道了。
4-2 LangVersion
填值低于框架支持版本
這個(gè)的話,Visual Studio 也是允許的,只不過(guò)這樣的情況下,由于你該框架無(wú)法使用超過(guò)你填入的這個(gè)語(yǔ)言版本的新語(yǔ)法,因此部分語(yǔ)法編譯器會(huì)不讓你編譯通過(guò)。但是,API 本身來(lái)說(shuō)是足夠的,所以不存在前面這種情況。
按道理來(lái)講,我們沒(méi)有必要這么去逆向修改該數(shù)值,因?yàn)樾抡Z(yǔ)法你其實(shí)可用可不用的,不用也不影響什么,所以不用刻意去修改為低版本來(lái)約束自己不用新語(yǔ)法,沒(méi)有必要。
Part 5 本板塊的 roadmap
接下來(lái)說(shuō)一下“C# 新語(yǔ)法詳解”這個(gè)板塊接下來(lái)的安排(Roadmap)。接下來(lái)我會(huì)按照微軟貼出來(lái)的語(yǔ)法文檔的順序,從 C# 2 開(kāi)始逐步給大家介紹新的語(yǔ)法內(nèi)容,以及它的使用方式、原理。有些復(fù)雜的語(yǔ)法我們不會(huì)一講講完,但有些語(yǔ)法極為簡(jiǎn)單可能壓根都不必寫成一個(gè)單獨(dú)的一講。我的安排是這樣的:所有的無(wú)法一講講完的語(yǔ)法都分若干講慢慢給大家介紹,但沒(méi)有必要寫成一講的語(yǔ)法,我仍然采用以一講為單位的形式給大家介紹,這樣是為了大家以后查閱資料和文檔更加方便這么做的,絕對(duì)不是我為了湊講數(shù),絕對(duì)不是!
大概數(shù)了一下,C# 新語(yǔ)法也挺多的,絕對(duì)不是只有一二十個(gè)新語(yǔ)法,所以慢慢來(lái)吧。從 C# 2 開(kāi)始,我們會(huì)逐步給大家都介紹到的。下一講內(nèi)容我們將開(kāi)始第一個(gè)新語(yǔ)法的講解:泛型。