.NET Standard 來(lái)日苦短去日長(zhǎng)
自從 .NET 5 宣貫以來(lái),很多人都在問(wèn)這對(duì) .NET Standard 意味著什么,它是否仍然重要。在這篇文章中,我將解釋 .NET 5 是如何改進(jìn)代碼共用并取代 .NET Standard 的,我還將介紹什么情況下你仍然需要 .NET Standard。
概要
.NET 5 將是一個(gè)具有統(tǒng)一功能和 API 的單一產(chǎn)品,可用于 Windows 桌面應(yīng)用程序、跨平臺(tái)移動(dòng)應(yīng)用程序、控制臺(tái)應(yīng)用程序、云服務(wù)和網(wǎng)站。

為了更好地說(shuō)明這一點(diǎn),我們更新了這篇[1]關(guān)于 TFM (Target Framework Names) 介紹的文章(譯文:.NET 5 中 Target Framework 詳解),現(xiàn)支持的 TFM 如下:
.net5.0,表示代碼可在任意平臺(tái)運(yùn)行,它合并并替換了 netcoreapp 和 netstandard 這兩個(gè)名稱。這個(gè) TFM 通常只包括跨平臺(tái)的技術(shù)(除了一些為了滿足實(shí)用性而作出讓步的 API,就像我們?cè)?.NET Standard 中所做的那樣)。
net5.0-windows(還有后面會(huì)增加的net6.0-android 和 net6.0-ios),這些 TFM 表示 .NET 5 特定于操作系統(tǒng)的風(fēng)格,包含 net5.0 和特定于操作系統(tǒng)的功能。
我們不會(huì)再發(fā)布 .NET Standard 的新版本,但是 .NET 5 和所有未來(lái)的版本將繼續(xù)支持 .NET Standard 2.1 和更早的版本。你應(yīng)該將 net5.0(和未來(lái)的版本)視為共享代碼的基礎(chǔ)。
由于 net5.0 是所有這些新 TFM 的共用的基礎(chǔ),這意味著運(yùn)行時(shí)、庫(kù)和新的語(yǔ)言特性都會(huì)圍繞這個(gè)版本號(hào)進(jìn)行協(xié)調(diào)。例如,為了使用 C# 9,你需要使用 net5.0 或 net5.0-windows。
如何選擇 Target
.NET 5 和所有未來(lái)的版本將繼續(xù)支持 .NET Standard 2.1 和更早的版本,從 .NET Standard 重新 Target 到 .NET 5 的唯一原因是為了獲得更多運(yùn)行時(shí)特性、語(yǔ)言特性或 API 支持。所以,你可以把 .NET 5 想象成 .NET Standard 的 vNext。
那新代碼呢?該從 .NET Standard 2.0 開(kāi)始還是直接從 .NET 5 開(kāi)始?這得視情況而定。
應(yīng)用程序組件,如果你要將你的應(yīng)用程序以類庫(kù)的形式分解成多個(gè)組件,我建議將 netX.Y 作為 TFM,netX.Y 中的 X.Y 是應(yīng)用程序(或多個(gè)應(yīng)用程序)的 .NET 最低版本號(hào)。為了簡(jiǎn)單起見(jiàn),你可能希望所有組成你的應(yīng)用程序的 Project 都使用相同的 .NET 版本,因?yàn)檫@樣可以保證各處的代碼都可以使用相同的 BCL 特性。
可重用庫(kù),如果你正在構(gòu)建計(jì)劃在 NuGet 上發(fā)布的可重用庫(kù),你將需要考慮適用范圍和可用新特性之間的權(quán)衡。.NET Standard 2.0 是 .NET Framework 支持的最高 .NET Standard 版本,所以它可以滿足你的大部分使用場(chǎng)景。我們通常建議不要將 Target 鎖定在 .NET Standard 1.x 上,因?yàn)椴恢档迷贋榇嗽鎏聿槐匾穆闊?。如果你不需要支?.NET Framwork,那么你可以選擇 .NET Standard 2.1 或者 .NET 5,大多數(shù)代碼可能可以跳過(guò) .NET Standard 2.1 直接轉(zhuǎn)到 .NET 5。
那么,你應(yīng)該怎么做呢?我的建議是,已被廣泛使用的庫(kù)可能需要同時(shí)提供 .NET Standard 2.0 和 .NET 5 支持。支持 .NET Standard 2.0 將使你的庫(kù)適用性更廣,而支持 .NET 5 則確保你可以為已經(jīng)在 .NET 5 上的用戶使用最新的平臺(tái)特性。
幾年后,可重用庫(kù)的選擇將只涉及 netX.Y 版本,這基本上是構(gòu)建 .NET 庫(kù)的一慣做法——你通常要支持一段時(shí)間較老的版本,以確保沒(méi)有升級(jí)最新 .NET 版本的用戶依然可以使用你的庫(kù)。
總結(jié)一下:
- 在 .NET Framework 和所有其他平臺(tái)之間共享代碼,使用 netstandard2.0。
- 在 Mono、Xamarin 和 .NET Core 3.x 之間共享代碼,使用 netstandard2.1。
- 往后的共享代碼,使用 net5.0。
.NET 5 如何解決 .NET Standard 存在的問(wèn)題
.NET Standard 使得創(chuàng)建適用于所有 .NET 平臺(tái)的庫(kù)變得更加容易,但是 .NET Standard 仍然存在三個(gè)問(wèn)題:
它的版本更新很慢[2],這意味著你不能輕松地使用最新的特性。
它需要一個(gè)解碼環(huán)[3]來(lái)將版本映射到 .NET 實(shí)現(xiàn)。
它公開(kāi)了特定于平臺(tái)的特性[4],這意味著你不能靜態(tài)地驗(yàn)證代碼是否真正可移植。
讓我們看看 .NET 5 將如何解決這三個(gè)問(wèn)題。
問(wèn)題 1:.NET Standard 版本更新慢
在設(shè)計(jì) .NET Standard[5] 時(shí),.NET 平臺(tái)還沒(méi)有在實(shí)現(xiàn)層次上融合,這使得編寫(xiě)需要在不同環(huán)境下工作的代碼變得困難,因?yàn)椴煌墓ぷ鞔a使用的是不同的 .NET 實(shí)現(xiàn)。
.NET Standard 的目標(biāo)是統(tǒng)一基礎(chǔ)類庫(kù)(BCL)的特性集,這樣你就可以編寫(xiě)一個(gè)可以在任何地方運(yùn)行的單一庫(kù)。這為我們提供了很好的服務(wù):前 1000 個(gè)軟件包中有超過(guò) 77% 支持 .NET Standard。如果我們看看 NuGet.org 上所有在過(guò)去 6 個(gè)月里更新過(guò)的軟件包,采用率是 58%。

但是只標(biāo)準(zhǔn)化 API 就會(huì)產(chǎn)生額外的付出,它要求我們?cè)谔砑有?API 時(shí)進(jìn)行協(xié)調(diào)——這一直在發(fā)生。.NET 開(kāi)源社區(qū)(包括.NET 團(tuán)隊(duì))通過(guò)提供新的語(yǔ)言特性、可用性改進(jìn)、新的交叉(cross-cutting)功能(如 Span)或支持新的數(shù)據(jù)格式或網(wǎng)絡(luò)協(xié)議,不斷對(duì) BCL 進(jìn)行創(chuàng)新。
而我們雖然可以以 NuGet 包的形式提供新的類型,但不能以這種方式在現(xiàn)有類型上提供新的 API。所以,從一般意義上講,BCL 的創(chuàng)新需要發(fā)布新版本的 .NET 標(biāo)準(zhǔn)。
在 .NET Standard 2.0 之前,這并不是一個(gè)真正的問(wèn)題,因?yàn)槲覀冎粚?duì)現(xiàn)有的 API 進(jìn)行標(biāo)準(zhǔn)化。但在 .NET Standard 2.1 中,我們對(duì)全新的 API 進(jìn)行了標(biāo)準(zhǔn)化,這也是我們看到相當(dāng)多摩擦的地方。
這種摩擦從何而來(lái)?
.NET 標(biāo)準(zhǔn)是一個(gè) API 集,所有的.NET 實(shí)現(xiàn)都必須支持,所以它有一個(gè)編輯方面[6]的問(wèn)題,所有的 API 必須由 .NET Standard 審查委員會(huì)[7]審查。該委員會(huì)由 .NET 平臺(tái)實(shí)現(xiàn)者以及 .NET 社區(qū)的代表組成。其目標(biāo)是只對(duì)我們能夠真正在所有當(dāng)前和未來(lái)的 .NET 平臺(tái)中實(shí)現(xiàn)的 API 進(jìn)行標(biāo)準(zhǔn)化。這些審查是必要的,因?yàn)?.NET 協(xié)議棧有不同的實(shí)現(xiàn),有不同的限制。
我們預(yù)測(cè)到了這種類型的摩擦,這就是為什么我們很早就說(shuō)過(guò),.NET 標(biāo)準(zhǔn)將只對(duì)至少一個(gè) .NET 實(shí)現(xiàn)中已經(jīng)推出的 API 進(jìn)行標(biāo)準(zhǔn)化。這乍一看似乎很合理,但隨后你就會(huì)意識(shí)到,.NET Standard 不可能頻繁地更新。所以,如果一個(gè)功能錯(cuò)過(guò)了某個(gè)特定的版本,你可能要等上幾年才能使用,甚至可能要等更久,直到這個(gè)版本的 .NET Standard 得到廣泛支持。
我們覺(jué)得對(duì)于某些特性來(lái)說(shuō),機(jī)會(huì)損失太大,所以我們做了一些不自然的行為,將還沒(méi)有推出的 API 標(biāo)準(zhǔn)化(比如 AsyncEnumerable)。對(duì)所有的功能都這樣做實(shí)在是太昂貴了,這也是為什么有不少功能還是錯(cuò)過(guò)了 .NET Standard 2.1 這趟列車(chē)的原因(比如新的硬件特性)。
但如果有一個(gè)單一的代碼庫(kù)呢?如果這個(gè)代碼庫(kù)必須支持所有與 .NET 至今所實(shí)現(xiàn)功能有所不同的特性,比如同時(shí)支持及時(shí)編譯(JIT)和超前編譯(AOT)呢?
與其在事后才進(jìn)行這些審查,不如從一開(kāi)始就將所有這些方面作為功能設(shè)計(jì)的一部分。在這樣的世界里,標(biāo)準(zhǔn)化的 API 集從構(gòu)造上來(lái)說(shuō),就是通用的 API 集。當(dāng)一個(gè)功能實(shí)現(xiàn)后,因?yàn)榇a庫(kù)是共享的,所以大家就已經(jīng)可以使用了。
問(wèn)題 2:.NET Standard 需要解碼環(huán)
將 API 集與它的實(shí)現(xiàn)分離,不僅僅是減緩了 API 的可用性,這也意味著我們需要將 .NET Standard 版本映射到它們的實(shí)現(xiàn)上[3]。作為一個(gè)長(zhǎng)期以來(lái)不得不向許多人解釋這個(gè)表格的人,我已經(jīng)意識(shí)到這個(gè)看似簡(jiǎn)單的想法是多么復(fù)雜。我們已經(jīng)盡力讓它變得更簡(jiǎn)單,但最終,這種復(fù)雜性是與生俱來(lái)的,因?yàn)? API 集和實(shí)現(xiàn)是獨(dú)立發(fā)布的。
我們統(tǒng)一了 .NET 平臺(tái),在它們下面又增加了一個(gè)合成平臺(tái),代表了通用的 API 集。從很現(xiàn)實(shí)的意義上來(lái)說(shuō),這幅漫畫(huà)是很到位的表達(dá)了這個(gè)痛點(diǎn):

如果不能實(shí)現(xiàn)真正意義上的合并,我們就無(wú)法解決這個(gè)問(wèn)題,這正是 .NET 5 所做的:它提供了一個(gè)統(tǒng)一的實(shí)現(xiàn),各方都建立在相同的基礎(chǔ)上,從而得到相同的 API 和版本號(hào)。
問(wèn)題 3:.NET Standard 公開(kāi)了特定平臺(tái) API
當(dāng)我們?cè)O(shè)計(jì) .NET Standard 時(shí),為了避免過(guò)多地破壞庫(kù)的生態(tài)系統(tǒng),我們不得不做出讓步[4]。也就是說(shuō),我們不得不包含一些 Windows 專用的 API(如文件系統(tǒng) ACL、注冊(cè)表、WMI 等)。今后,我們將避免在 net5.0、net6.0 和未來(lái)的版本中加入特定平臺(tái)的 API。然而,我們不可能預(yù)測(cè)未來(lái)。例如,我們最近為 Blazor WebAssembly 增加了一個(gè)新的 .NET 運(yùn)行環(huán)境,在這個(gè)環(huán)境中,一些原本跨平臺(tái)的 API(如線程或進(jìn)程控制)無(wú)法在瀏覽器的沙箱中得到支持。
很多人抱怨說(shuō),這類 API 感覺(jué)就像“地雷”--代碼編譯時(shí)沒(méi)有錯(cuò)誤,因此看起來(lái)可以移植到任何平臺(tái)上,但當(dāng)運(yùn)行在一個(gè)沒(méi)有給定 API 實(shí)現(xiàn)的平臺(tái)上時(shí),就會(huì)出現(xiàn)運(yùn)行時(shí)錯(cuò)誤。
從 .NET 5 開(kāi)始,我們將提供隨 SDK 發(fā)布的默認(rèn)開(kāi)啟的分析器和代碼修復(fù)器。它包含平臺(tái)兼容性分析器,可以檢測(cè)無(wú)意中使用了目標(biāo)平臺(tái)并不支持的 API。這個(gè)功能取代了 Microsoft.DotNet.Analyzers.Compatibility NuGet 包。
讓我們先來(lái)看看 Windows 特有的 API。
- 處理 Windows 特定 API
當(dāng)你創(chuàng)建一個(gè) Target 為 net5.0 為目標(biāo)的項(xiàng)目時(shí),你可以引用 Microsoft.Win32.Registry 包。但當(dāng)你開(kāi)始使用它時(shí):
- private static string GetLoggingDirectory()
- {
- using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Fabrikam"))
- {
- if (key?.GetValue("LoggingDirectoryPath") is string configuredPath)
- return configuredPath;
- }
- string exePath = Process.GetCurrentProcess().MainModule.FileName;
- string folder = Path.GetDirectoryName(exePath);
- return Path.Combine(folder, "Logging");
- }
你會(huì)得到以下警告:
- CA1416: 'RegistryKey.OpenSubKey(string)' is supported on 'windows'
- CA1416: 'Registry.CurrentUser' is supported on 'windows'
- CA1416: 'RegistryKey.GetValue(string?)' is supported on 'windows'
你有三個(gè)選擇來(lái)處理這些警告。
- 調(diào)用保護(hù):在調(diào)用 API 之前,你可以使用 OperatingSystem.IsWindows() 來(lái)檢查當(dāng)前運(yùn)行環(huán)境是否是 Windows 系統(tǒng)。
- 將調(diào)用標(biāo)記為 Windows 專用:在某些情況下,通過(guò) [SupportedOSPlatform("windows")] 將調(diào)用成員標(biāo)記為特定平臺(tái)也有一定的意義。
- 刪除代碼:一般來(lái)說(shuō),這不是你想要的,因?yàn)檫@意味著當(dāng)你的代碼被 Windows 用戶使用時(shí),你會(huì)失去保真度(fidelity)。但對(duì)于存在跨平臺(tái)替代方案的情況,你應(yīng)該盡可能使用跨平臺(tái)方案,而不是平臺(tái)特定的 API。例如,你可以使用一個(gè) XML 配置文件來(lái)代替使用注冊(cè)表。
- 抑制警告:當(dāng)然,你可以通過(guò) .editorconfig 或 #pragma warning disable 來(lái)抑制警告。然而,當(dāng)使用特定平臺(tái)的 API 時(shí),你應(yīng)該更喜歡選項(xiàng) (1) 和 (2)。
為了調(diào)用保護(hù),可以使用 System.OperatingSystem 類上的新靜態(tài)方法,示例:
- private static string GetLoggingDirectory()
- {
- if (OperatingSystem.IsWindows())
- {
- using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Fabrikam"))
- {
- if (key?.GetValue("LoggingDirectoryPath") is string configuredPath)
- return configuredPath;
- }
- }
- string exePath = Process.GetCurrentProcess().MainModule.FileName;
- string folder = Path.GetDirectoryName(exePath);
- return Path.Combine(folder, "Logging");
- }
要將你的代碼標(biāo)記為 Windows 專用,請(qǐng)應(yīng)用新的 SupportedOSPlatform 屬性:
- [SupportedOSPlatform("windows")]
- private static string GetLoggingDirectory()
- {
- using (RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Fabrikam"))
- {
- if (key?.GetValue("LoggingDirectoryPath") is string configuredPath)
- return configuredPath;
- }
- string exePath = Process.GetCurrentProcess().MainModule.FileName;
- string folder = Path.GetDirectoryName(exePath);
- return Path.Combine(folder, "Logging");
- }
在這兩種情況下,使用注冊(cè)表的警告都會(huì)消失。
關(guān)鍵的區(qū)別在于,在第二個(gè)例子中,分析器現(xiàn)在會(huì)對(duì) GetLoggingDirectory() 的調(diào)用發(fā)出警告,因?yàn)樗F(xiàn)在被認(rèn)為是 Windows 特有的 API。換句話說(shuō),你把平臺(tái)檢查的要求轉(zhuǎn)給調(diào)用者放去做了。
[SupportedOSPlatform] 屬性可以應(yīng)用于成員、類型和程序集級(jí)別。這個(gè)屬性也被 BCL 本身使用,例如,程序集 Microsoft.Win32.Registry 就應(yīng)用了這個(gè)屬性,這也是分析器最先就知道注冊(cè)表是 Windows 特定 API 方法的原因。
請(qǐng)注意,如果你的目標(biāo)是 net5.0-windows,這個(gè)屬性會(huì)自動(dòng)應(yīng)用到你的程序集中。這意味著使用 net5.0-windows 的 Windows 專用 API 永遠(yuǎn)不會(huì)產(chǎn)生任何警告,因?yàn)槟愕恼麄€(gè)程序集被認(rèn)為是 Windows 專用的。
- 處理 Blazor WebAssembly 不支持的 API
Blazor WebAssembly 項(xiàng)目在瀏覽器沙盒內(nèi)運(yùn)行,這限制了你可以使用的 API。例如,雖然線程和進(jìn)程創(chuàng)建都是跨平臺(tái)的 API,但我們無(wú)法讓這些 API 在 Blazor WebAssembly 中工作,它們會(huì)拋出 PlatformNotSupportedException。我們已經(jīng)用 [UnsupportedOSPlatform("browser")] 標(biāo)記了這些 API。
假設(shè)你將 GetLoggingDirectory() 方法復(fù)制并粘貼到 Blazor WebAssembly 應(yīng)用程序中:
- private static string GetLoggingDirectory()
- {
- //...
- string exePath = Process.GetCurrentProcess().MainModule.FileName;
- string folder = Path.GetDirectoryName(exePath);
- return Path.Combine(folder, "Logging");
- }
你將得到以下警告:
- CA1416 'Process.GetCurrentProcess()' is unsupported on 'browser'
- CA1416 'Process.MainModule' is unsupported on 'browser'
你可以用與 Windows 特定 API 基本相同的做法來(lái)處理這些警告。
你可以對(duì)調(diào)用進(jìn)行保護(hù):
- private static string GetLoggingDirectory()
- {
- //...
- if (!OperatingSystem.IsBrowser())
- {
- string exePath = Process.GetCurrentProcess().MainModule.FileName;
- string folder = Path.GetDirectoryName(exePath);
- return Path.Combine(folder, "Logging");
- }
- else
- {
- return string.Empty;
- }
- }
或者你可以將該成員標(biāo)記為不被 Blazor WebAssembly 支持:
- [UnsupportedOSPlatform("browser")]
- private static string GetLoggingDirectory()
- {
- //...
- string exePath = Process.GetCurrentProcess().MainModule.FileName;
- string folder = Path.GetDirectoryName(exePath);
- return Path.Combine(folder, "Logging");
- }
由于瀏覽器沙盒的限制性相當(dāng)大,所以并不是所有的類庫(kù)和 NuGet 包都能在 Blazor WebAssembly 中運(yùn)行。此外,絕大多數(shù)的庫(kù)也不應(yīng)該支持在 Blazor WebAssembly 中運(yùn)行。
這就是為什么針對(duì) net5.0 的普通類庫(kù)不會(huì)看到不支持 Blazor WebAssembly API 的警告。你必須在項(xiàng)目文件中添加 項(xiàng),明確表示你打算在 Blazor WebAssembly 中支持您的項(xiàng)目:
- <Project Sdk="Microsoft.NET.Sdk">
- <PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
- </PropertyGroup>
- <ItemGroup>
- <SupportedPlatform Include="browser" />
- </ItemGroup>
- </Project>
如果你正在構(gòu)建一個(gè) Blazor WebAssembly 應(yīng)用程序,你不必這樣做,因?yàn)? Microsoft.NET.Sdk.BlazorWebAssembly SDK 會(huì)自動(dòng)做到這一點(diǎn)。
.NET 5 是 .NET Standard 和 .NET Core 的結(jié)合
.NET 5 及后續(xù)版本將是一個(gè)單一的代碼庫(kù),支持桌面應(yīng)用、移動(dòng)應(yīng)用、云服務(wù)、網(wǎng)站以及未來(lái)的任何 .NET 運(yùn)行環(huán)境。
你可能會(huì)想“等等,這聽(tīng)起來(lái)很不錯(cuò),但如果有人想創(chuàng)建一個(gè)全新的實(shí)現(xiàn)呢”。這也是可以的。但幾乎沒(méi)有人會(huì)從頭開(kāi)始一個(gè)新的實(shí)現(xiàn)。最有可能的是,它將是當(dāng)前代碼庫(kù)(dotnet/runtime[8])的一個(gè)分支。例如,Tizen(三星智能家電平臺(tái))使用的是 .NET Core,只做了細(xì)小的改動(dòng),并在上面使用了三星特有的應(yīng)用模型。
Fork 保留了合并關(guān)系,這使得維護(hù)者可以不斷從 dotnet/runtime[8] 倉(cāng)庫(kù)中拉取新的變化,在不受其變化影響的領(lǐng)域受益于 BCL 創(chuàng)新,這和 Linux 發(fā)行版的工作方式非常相似。
當(dāng)然,在某些情況下,人們可能希望創(chuàng)建一個(gè)非常不同的“種類”的 .NET,比如一個(gè)沒(méi)有當(dāng)前 BCL 的最小運(yùn)行時(shí)。但這意味著它不能利用現(xiàn)有的 .NET 庫(kù)生態(tài)系統(tǒng),它也不會(huì)實(shí)現(xiàn) .NET Standard。我們一般對(duì)這個(gè)方向的追求不感興趣,但 .NET Standard 和 .NET Core 的結(jié)合并不妨礙這一點(diǎn),也不會(huì)增加難度。
.NET 版本
作為一個(gè)庫(kù)作者,你可能想知道 .NET 5 什么時(shí)候能得到廣泛支持。今后,我們將在每年的 11 月發(fā)布 .NET 新版本,每隔一年發(fā)布一次長(zhǎng)期支持(LTS)版本。
.NET 5 將在 2020 年 11 月正式發(fā)布,而 .NET 6 將在 2021 年 11 月作為 LTS 發(fā)布。我們創(chuàng)建了這個(gè)固定的時(shí)間表,使你更容易規(guī)劃您的更新(如果你是應(yīng)用程序開(kāi)發(fā)人員),并預(yù)測(cè)對(duì)支持的 .NET 版本的需求(如果你是庫(kù)開(kāi)發(fā)人員)。
得益于 .NET Core 的并行安裝(譯注:一臺(tái)機(jī)器可同時(shí)安裝多個(gè) .NET Core 版本,且向下兼容),它的新版本被采用速度相當(dāng)快,其中 LTS 版本最受歡迎。事實(shí)上,.NET Core 3.1 是有史以來(lái)采用最快的 .NET 版本。

我們的期望是,每次發(fā)布(大版本)時(shí),我們都會(huì)把所有框架名稱連在一起發(fā)布。例如,它可能看起來(lái)像這樣:

這意味著你心里可以有個(gè)預(yù)期,無(wú)論我們?cè)?BCL 中做了什么創(chuàng)新,你都能在所有的應(yīng)用模型中使用它,無(wú)論它們運(yùn)行在哪個(gè)平臺(tái)上。這也意味著,只要你運(yùn)行最新版本的庫(kù),你總是可以在所有的應(yīng)用模型消費(fèi)最新的 net 框架帶來(lái)的庫(kù)。
這種模式消除了圍繞 .NET Standard 版本的復(fù)雜性,因?yàn)槊看挝覀儼l(fā)布時(shí),你都可以假設(shè)所有的平臺(tái)都會(huì)立即和完全支持新版本,而我們通過(guò)使用前綴命名慣例來(lái)鞏固這一承諾。
.NET 的新版本可能會(huì)添加對(duì)其他平臺(tái)的支持。例如,我們將通過(guò) .NET 6 增加對(duì) Android 和 iOS 的支持。相反,我們可能會(huì)停止支持那些不再相關(guān)的平臺(tái)。這一點(diǎn)可以通過(guò)在 .NET 6 中不存在的 net5.0-someoldos 目標(biāo)框架來(lái)說(shuō)明。我們目前沒(méi)有放棄一個(gè)平臺(tái)支持的計(jì)劃,那將是一個(gè)大問(wèn)題,這不是預(yù)期的,若有我們會(huì)提前很久宣布。這也是我們對(duì) .NET Standard 的模式,例如,沒(méi)有新版本的 Windows Phone 實(shí)現(xiàn)了后面的 .NET Standard 版本。
為什么沒(méi)有 WebAssembly 的 TFM
我們最初考慮為 WebAssembly 添加 TFM,如 net5.0-wasm。后來(lái)我們決定不這么做,原因如下:
- WebAssembly 更像是一個(gè)指令集(如 x86 或 x64),而不是像一個(gè)操作系統(tǒng),而且我們一般不提供不同架構(gòu)之間有分歧的 API。
- WebAssembly 在瀏覽器沙箱中的執(zhí)行模型是一個(gè)關(guān)鍵的差異化,但我們決定只將其建模為運(yùn)行時(shí)檢查更有意義。類似于你對(duì) Windows 和 Linux 的檢查方式,你可以使用 OperatingSystem 類型。由于與指令集無(wú)關(guān),所以該方法被稱為 IsBrowser() 而不是 IsWebAssembly()。
- WebAssembly 有運(yùn)行時(shí)標(biāo)識(shí)符(RID)[9],稱為 browser 和 browser-wasm。它們?cè)试S包的作者在瀏覽器中針對(duì) WebAssembly 部署不同的二進(jìn)制文件。這對(duì)于需要事先編譯成 WebAssembly 的本地代碼特別有用。
如上所述,我們已經(jīng)標(biāo)記了在瀏覽器沙盒中不支持的 API,例如 System.Diagnostics.Process。如果你從瀏覽器應(yīng)用內(nèi)部使用這些 API,你會(huì)得到一個(gè)警告,告訴你這個(gè) API 是不支持的。
總結(jié)
net5.0 是為能在任何平臺(tái)運(yùn)行的代碼而設(shè)計(jì)的,它結(jié)合并取代了 netcoreapp 和 netstandard 名稱。我們還有針對(duì)特定平臺(tái)的框架,比如 net5.0-windows(后面還有 net6.0-android,net6.0-ios)。
由于標(biāo)準(zhǔn)和它的實(shí)現(xiàn)之間沒(méi)有區(qū)別,你將能夠比使用 .NET Standard 更快地利用新功能。而且由于命名慣例,你將能夠很容易地知道誰(shuí)可以使用一個(gè)給定的庫(kù)--而無(wú)需查閱 .NET Standard 版本表。
雖然 .NET Standard 2.1 將是 .NET Standard 的最后一個(gè)版本,但 .NET 5 和所有未來(lái)的版本將繼續(xù)支持.NET Standard 2.1 和更早的版本。你應(yīng)該將 net5.0(以及未來(lái)的版本)視為未來(lái)共享代碼的基礎(chǔ)。
祝,編碼愉快!