.NET 5.0正式發布,功能特性介紹(翻譯)

本文由葡萄城技術團隊翻譯並首發

轉載請註明出處:葡萄城官網,葡萄城為開發者提供專業的開發工具、解決方案和服務,賦能開發者。

 

我們很高興今天.NET5.0正式發布。這是一個重要的版本—其中也包括了C# 9和F# 5大量新特性和優秀的改進。微軟和其他公司的團隊已經在生產和性能測試環境中開始使用了。這些團隊向我們回饋的結果比較令人滿意,它證明了對性能提升及降低Web應用託管成本的機會有積極的表現。從預覽版1開始,我們一直在5.0上運行我們自己的網站。從我們目前的所見所聞來看,.NET5.0無需在升級上花費太多的精力就能帶來巨大的價值。對於你的下一個應用來說,這是一個很好的選擇,而且可以直接從早期的.NET Core版本升級。我們希望您在台式機、筆記型電腦電腦和雲實例上正式開始使用它。

 

ASP.NET Core、EF Core、C#9和F#5也將在今天一同發布
您可以下載.NET5.0,適用於Windows、MacOS和Linux,適用於x86、x64、Arm32和Arm64。

對於Visual Studio用戶,您需要Visual Studio 16.8或更高的版本才能在Windows上使用.NET 5.0,在MacOS上使用最新版本的Visual Studio for Mac)。Visual Studio Code的C#擴展也已經支援.NET5.0和C#9。

NET 5.0是我們的.NET統一之旅的第一個版本。我們構建.NET 5.0是為了讓更多的開發人員能夠將他們的.NET Framework程式碼和應用程式遷移到.NET5.0。我們在5.0中也做了很多前期工作,以便Xamarin開發人員在發布.NET6.0時可以使用統一的.NET平台。在後面的文章中會有更多關於.NET統一的內容。

這個版本是完全開源的第五個主要的.NET版本。現在,在GitHub上的DotNet org中,有大量的個人和公司(包括.NET Foundation企業贊助商)作為一個大型社區在.NET的各個方面共同工作,.NET5.0中的改進是許多人通過他們的努力及創新的想法構成的結果,所有這些都超出了微軟對該項目的管理。為此,我們向所有為.NET 5.0(以及之前的版本)做出貢獻的人表示 「萬分感謝」!

我們早在2019年5月就引入了.NET5.0,當時甚至設定了2020年11月的發布日期,結果我們如期發布了,為此要感謝團隊中的每一個人,是他們讓這一切成為現實!
2021年11月我們還將發布.NET
6.0,今後每年的11月我們都將發布新的.NET版本。

.NET 5.0亮點

在.NET5.0中有許多重要的改進:


.NET5.0已經在dot.net和Bing.com上託管了幾個月,已經經過了數個月的實際測試。


許多組件的性能都得到了極大的提高,在.NET5.0中的性能改進、.NET5.0中的ARM64性能和GRPC中都有詳細描述。


C#9和F#5提供了新的語言改進,比如C# 9的頂級程式和記錄,而F# 5提供了互動式編程,.NET上函數式編程的性能得到了提升。


.NET庫增強了Json序列化、正則表達式和HTTP(HTTP 1.1、HTTP/2)的性能。


改進了GC、分層編譯和其他方面,P95延遲有所降低。


通過ClickOnce客戶端發布應用程式,單文件應用程式,減小的容器映像大小以及添加的Server Core容器映像,應用程式部署選項更好。


Windows Arm64和WebAssembly擴展了平台範圍。

我已經為.NET5.0寫了很多Demo。您可以看一下這些.NET5.0示例,以了解更多關於新的C#9和庫特性的資訊。

平台和Microsoft支援

對於Windows、MacOS和Linux,.Net 5.0的平台支援列表與.NET Core 3.1幾乎相同。如果您在受支援的作業系統上使用.NET Core
3.1,則應該能夠在該作業系統的大部分版本上採用.NET 5.0。在.NET 5.0中,最重要的新增功能是Windows Arm64。

.Net 5.0是當前版本。這意味著它將在.NET6.0發布後的三個月內得到支援。因此,我們預計到2022年2月中旬將支援.NET5.0。與.NET
Core 3.1一樣,.NET6.0將是一個LTS版本,並將支援三年。

平台統一的願景

去年,我們分享了一個統一的.NET生態系統的願景。它對您的價值在於,您將能夠使用同一組API、語言和工具來覆蓋廣泛的應用類型,其中包括移動、雲、桌面和物聯網。您可能已經意識到,現在您已經可以使用.NET面向廣泛的平台,但可能工具和API在Web和Mobile之間並不總是相同的,或者並不總是同時發布的。

作為.NET5.0和6.0的一部分,我們正在將.NET的產品體驗儘可能的進行統一,同時使您能夠選擇您想要使用的.NET平台的部分。如果你想以Mobile而不是WebAssembly為目標,你不需要下載WebAssembly工具,反之亦然。ASP.NET Core和WPF也是如此。您還可以通過更簡單的方式從命令行獲取所需的所有.NET工具以及構建和運行時包。我們正在為.NET平台組件提供包管理器體驗(包括使用現有的包管理器)。這對於很多場景來說都是很棒的。快速構建開發環境和CI / CD可能是最大的受益者。
實現這一願景的第一步是整合.NET repos,包括Mono的一個大子集。擁有一個用於運行時和.NET庫的repo是在任何地方交付相同產品的前提條件。它還有助於進行廣泛的更改,這些更改會影響運行時和庫,而這些庫以前是有repo邊界的。一些人擔心,大規模回購將更難管理。事實證明並非如此。

在.NET 5.0版本中,Blazor是利用回購整合和.NET統一的最佳例子。Blazor WebAssembly的運行時和庫現在是從合併的
DotNet/runtime repo所構建的。例如,這意味著伺服器上的Blazor
WebAssembly和Blazor對List<T>
將使用完全相同的程式碼。但在.NET5.0之前,Blazor並非如此。我們對Blazor WebAssembly採取的方法與我們在.NET6.0中使用Xamarin的方法非常相似。

.NET Framework仍然是受支援的Microsoft產品,並且每個新版本的Windows都將繼續支援它。我們去年宣布已經停止向.NET Framework添加新功能,並完成了向.NET Core添加.NET Framework API。這意味著現在是考慮將您的.NET
Framework應用程式遷移到.NET Core的好機會。對於.NET Framework客戶端開發人員,.NET5.0支援Windows窗體和WPF。我們從許多開發人員那裡聽說,從.NET Framework移植非常簡單。對於.NET Framework伺服器開發人員,您需要採用ASP.NET Core才能使用.NET 5.0。對於Web Forms開發人員,我們相信Blazor通過更高效、更現代化的實現提供了類似的開發體驗。WCF伺服器和工作流用戶可以查看支援這些框架的社區項目。從.NET Framework移植到.NET Core文檔是一個很好的起點。這就是說,如果你對自己的體驗滿意,那麼讓你的應用程式運行在.NET Framework上是一個很好的方法。

Windows團隊正致力於研究Reunion作為UWP及其相關技術的下一步。我們一直在與Reunion團隊合作,以確保.NET5.0及更高版本能夠很好地與WinUI和WebView2協同工作。

讓我們來看看5.0版本中的新特性。

語言

C#9和F#5是.NET5.0版本的一部分,包含在.NET5.0 SDK中。Visual Basic也包含在5.0 SDK中。它不包括語言更改,但進行了改進以支援.NET Core上的Visual Basic應用程式框架。

C#源程式碼生成器是一項重要的C#編譯器新特性。從技術上講,它們不是C#9的一部分,因為它沒有任何語言語法。請參閱新的C#源程式碼生成器示例,幫助您開始使用這一新功能。我們希望在.NET6.0及更高版本的.NET產品中更多地使用源程式碼生成器。

為了親身試用新版本,我們部分人決定更新DotNet/iot
Repo,以使用新的C#9語法並以.NET5.0嘗試目標。它使用頂級程式、記錄、模式和切換表達式。它也已更新,以利用.NET庫中完整的可為空的注釋集。我們還更新了.NET物聯網的文檔。我們將查看該repo中的幾個示例來探索C#9。

LED-BLINK程式是一個不錯的緊湊高級程式示例

using System;
using System.Device.Gpio;
using System.Threading;

var pin = 18;
var lightTime = 1000;
var dimTime = 200;

Console.WriteLine($"Let's blink an LED!");
using GpioController controller = new ();
controller.OpenPin(pin, PinMode.Output);
Console.WriteLine($"GPIO pin enabled for use: {pin}");

// turn LED on and off
while (true)
{
    Console.WriteLine($"Light for {lightTime}ms");
    controller.Write(pin, PinValue.High);
    Thread.Sleep(lightTime);

    Console.WriteLine($"Dim for {dimTime}ms");
    controller.Write(pin, PinValue.Low);
    Thread.Sleep(dimTime);
}

您可以看到target-typed的使用以及new對controller變數的分配。。GpioController類型僅在賦值的左側定義。類型是在右手邊推斷出來的。這種新語法是var的另一種選擇,var的類型只顯示在賦值的右側,並通過關鍵字var在左側推斷。

通過定義方法並利用在相同或其他文件中定義的類型,頂級程式也可能增加複雜性。CharacterLcd示例演示了其中一些功能。

邏輯和屬性模式

C# 9包括對新模型的支援。您可以在如下程式碼中看到關於這個邏輯模式的示例。

var threshChoice = Console.ReadKey();
Console.WriteLine();
if (threshChoice.KeyChar is 'Y' or 'y')
{
   TestThresholdAndInterrupt(ccs811);
}

另一種新模式是屬性模式。您可以在我的Mycroft資訊訪問6.0示例中看到幾個屬性檢查。以下程式碼摘自PN532 RFID和NFC讀取器示例。

if (pollingType is not { Length: <=15 })
{
      return null;
}

此程式碼測試pollingType(類型為byte[]?)。為空或包含>15個位元組。這是返回NULL之前需要測試的兩個錯誤條件。也可以將此測試編寫為pollingType為空或{Length:>15}。
我想再給你看兩個模型。第一個是Mcp25xxx
CAN匯流排
邏輯模式

public static byte GetRxBufferNumber(Address address) => address switch
{
    >= Address.RxB0D0 and <= Address.RxB0D7 => 0,
    >= Address.RxB1D0 and <= Address.RxB1D7 => 1,
    _ => throw new ArgumentException(nameof(address), $"Invalid address value {address}."),
};

第二個是Piezo蜂鳴器控制器中的邏輯模式

if (element is not NoteElement noteElement)
{
    // In case it's a pause element we have only just wait desired time.
    Thread.Sleep(durationInMilliseconds);
}
else
{
    // In case it's a note element we play it.
    var frequency = GetFrequency(noteElement.Note, noteElement.Octave);
    _buzzer.PlayTone(frequency, (int)(durationInMilliseconds * 0.7));
    Thread.Sleep((int)(durationInMilliseconds * 0.3));
}

記錄

C#9包括一個名為Record的新類。與常規類相比,它有許多優點,其中一半與更簡潔的語法有關。以下記錄取自Bh1745 RGB感測器綁定。

public record ChannelCompensationMultipliers(double Red, double Green, double Blue, double Clear);

然後在同一文件中稍晚一點使用它,語法很熟悉:

ChannelCompensationMultipliers = new (2.2, 1.0, 1.8, 10.0);

可為空性注釋的改進

現在,.NET庫完全為空性添加了注釋。這意味著如果您啟用nullability,您將從平台獲得更多類型資訊來指導您使用該功能。目前,還沒有對.NET文檔進行完整的注釋。例如,String.IsNullOrEmpty(String)應該被注釋為接受一個字元串?,而String.Split(Char[])的注釋是char[]?我們希望這個問題很快就能解決。完整的資訊可以在Soure.dot.net上找到,也可以通過Visual Studio中的F12元數據查找獲得。

System.Device.Gpio和Iot.Device.Bindings包(這兩個包的版本都是1.1.0)也作為此版本的一部分進行了注釋,使用了更新的.NET5.0注釋。這兩個庫都是多目標的,但是,我們使用5.0視圖為所有目標生成注釋。

我們還添加了新的注釋類型。大型類在從構造函數調用的幫助器方法中實例化對象成員是很常見的。C#編譯器不能遵循對對象賦值的調用流程。當退出構造函數時,它會認為該成員為空,並將使用CS8618發出警告。MemberNotNull屬性可以解決此問題。將該屬性應用於幫助器方法。然後,編譯器將看到您設置了此值,並意識到該方法是從構造函數調用的。MemberNotNullWhen類似。

您可以使用以下程式碼在BMxx80溫度感測器中看到MemberNotNull的示例。

[MemberNotNull(nameof(_calibrationData))]
private void ReadCalibrationData()
{
   switch (this)
   {
         case Bme280 _:
            _calibrationData = new Bme280CalibrationData();
            _controlRegister = (byte)Bmx280Register.CTRL_MEAS;
            break;
         case Bmp280 _:
            _calibrationData = new Bmp280CalibrationData();
            _controlRegister = (byte)Bmx280Register.CTRL_MEAS;
            break;
         case Bme680 _:
            _calibrationData = new Bme680CalibrationData();
            _controlRegister = (byte)Bme680Register.CTRL_MEAS;
            break;
         default:
            throw new Exception("Bmxx80 device not correctly configured. Could not find calibraton data.");
   }

   _calibrationData.ReadFromDevice(this);
}

實際程式碼使用條件編譯。這是因為該項目是多目標的,而該屬性僅在.NET5.0+中受支援。使用該屬性可以跳過運行時檢查(在構造函數中),否則將需要這些檢查來滿足可空性要求,就像早期的.NET版本一樣。

工具類

我們改進了Windows Forms 設計器,使其能在.NET5.0及更高版本中運行,更改了支援WinRT的方式,並進行了其他改進。

Windows窗體設計器

Windows窗體設計器(用於.NET Core 3.1和.NET5.0)已在Visual Studio 16.8中進行了更新,現在支援所有Windows窗體控制項。設計器包括您指導的所有設計器功能,包括:拖放、選擇、移動和調整大小、控制項的剪切/複製/粘貼/刪除、與屬性窗口的集成、事件生成等。數據綁定和對更廣泛的第三方控制項集的支援很快就會到來。

 

 

.NET 5.0目標框架

在.NET5.0中,我們更改了用於目標框架的方法。以下項目文件演示了新的.NET5.0目標框架。

 

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>

</Project>

  

新的net5.0表單比我們之前使用的樣式更緊湊、更直觀。此外,我們正在擴展目標框架以描述作業系統依賴關係。我們希望通過.NET 6.0中的Xamarin定位iOS和Android,從而推動了這一變化。
net5.0netcoreapp3.1

Windows桌面API(包括Windows窗體、WPF和WinRT)僅在面向net5.0-windows時可用。您可以指定作業系統版本,如net5.0-Windows7或net5.0-windows10.0.17763.0(適用於Windows 2018年10月更新)。如果您想要使用WinRT API,則需要瞄準Windows 10版本。

當使用新的net5.0-windows tfm時,跨平台的場景可能會更具挑戰性。例如,System.Device.Gpio演示了一種用於管理Windows目標框架的模式,例如,如果您希望避免為Windows構建或避免在Linux上拉取Windows運行時包。

更新摘要:

  • l  Net5.0是.NET5.0的新目標框架Moniker (TFM)。
  • l  Net5.0結合併取代了netcoreapp和netStandard tfms。
  • l  Net5.0支援.NET Framework兼容模式。
  • l  Net5.0-Windows將用於公開特定於Windows的功能,包括Windows Forms、WPF和WinRT API。
  • l  特定於作業系統的TFMS可以包括作業系統版本號,如net6.0-ios14。
  • l  像ASP.NET Core這樣的可移植API將可以在net5.0上使用。同樣的情況也適用於Net6.0的Xamarin Forms。

Visual Studio 16.8中的模板仍然以.NET Core 3.1為目標,用於控制台、WPF和Windows窗體應用程式。ASP.NET模板已更新為支援.NET5.0。我們將在Visual Studio 16.9中更新其餘模板的模板。

WinRT Interop(重大更改)

關於以Windows API為目標的主題,我們已經轉向了一個新的模型,將WinRT API作為.NET5.0的一部分來支援。這包括調用API(雙向;CLR<==>WinRT),兩個類型系統之間的數據封送處理,以及要在類型系統或ABI邊界上被同等對待的類型的統一(即「投影類型」;IEnumerable和IIterable就是例子)。

現有的WinRT互作業系統已作為.NET5.0的一部分從.NET運行時中移除。這是一個突破性的變化。這意味著使用WinRT和.NET Core 3.x的應用程式和庫需要重新構建,不能按原樣在.NET5.0上運行。使用WinRT API的庫需要多目標來管理.NET Core 3.1和.NET5.0之間的這種差異。

展望未來,我們將依靠WinRT團隊在Windows中提供的新CsWinRT工具。它生成基於C#的WinRT互操作程式集,這些程式集可以通過NuGet交付。這正是Windows團隊正在為Windows中的WinRT API所做的事情。任何想要使用WinRT(在Windows上)作為互作業系統的人都可以使用該工具,以將本機API公開給.NET或將.NETAPI公開給本機程式碼。

CsWinRT工具在邏輯上類似於tlbimp和tlbexp,但要好得多。TLB工具依賴於.NET運行時中的大量COM互操作管道。CsWinRT工具只依賴於公共的.NETAPI。也就是說,C#9中的函數指針功能–在.NET5.0運行時中部分實現了–在一定程度上是受到CsWinRT工具需求的啟發。

這種新的WinRT互操作模型有幾個好處:

  • l  它可以獨立於.NET運行時進行開發和改進。
  • l  它與為iOS和Android等其他作業系統提供的基於工具的互作業系統是對稱的。
  • l  該工具可以利用其他.NET特性(AOT、C#特性、IL鏈接),而這在以前的系統中不是一個選項。
  • l  簡化了.NET運行時程式碼庫。

使用WinRT API不需要添加NuGet引用。以Windows10TFM為目標–剛才在.NET5.0TFM一節中已經討論過了–已經足夠了。如果您的目標是.NET Core 3.1或更早版本,則需要引用WinRT包。您可以在System.Device.Gpio項目中看到此模式。

原生導出

很長一段時間以來,我們一直要求為調用.NET程式碼的本機二進位文件啟用導出。該場景的構建塊是託管對UnManagedCeller sOnlyAttribute的API支援。

此功能是創建更高級別體驗的構建塊。我們團隊中的Aaron
Robinson一直致力於一個.NET Native Exports項目,該項目為將.NET組件發布為本機庫提供了更完整的體驗。我們正在尋找有關此功能的回饋,以幫助決定是否應將該方法包含在產品中。

.NET原生導出項目使您能夠:

  • l  公開自定義本機導出。
  • l  不需要像COM這樣的更高級別的互操作技術。
  • l  跨平台工作。
  • 有一些現有的項目支援類似的場景,例如:

  • 不受管理的出口。
  • l  DllExport

多年來,我們在本機應用程式中看到了各種.NET託管模型。@rseanHall為此提出並實現了一種新穎的新模型,該模型利用了.NET應用程式託管層提供的所有內置應用程式功能(特別是載入依賴項),同時允許從本機程式碼調用自定義入口點。這在很多情況下都是完美的,可以想像在從本機應用程式託管.NET組件的開發人員中變得流行起來。這在以前是不存在的。謝謝你的貢獻,@rseanHall。

兩個主要PR:

  • l  啟用從應用上下文調用Get_Runtime_Delegate。
  • l  實現HDT_GET_Function_POINTER

事件管道
事件管道是我們在.NET Core 2.2中添加的一個新的子系統和API,它可以在任何作業系統上執行性能和其他診斷調查。在.NET5.0中,事件管道已得到擴展,使探查器能夠編寫事件管道事件。此場景對於檢測以前依賴ETW(在Windows上)監視應用程式行為和性能的探查器至關重要。

現在可以通過事件管道獲得程式集載入資訊。這一改進是使類似的診斷功能(例如Fusion Log Viewer)成為.NET Framework的一部分的開始。現在,您可以使用以下命令,使用Dotnet-TRACE來收集此資訊:

dotnet-trace collect --providers Microsoft-Windows-DotNETRuntime:4:4 -- ./MyApp –my-arg 1

該工作流程在DotNet-TRACE文檔中進行了說明。您可以看到簡單測試應用程式的程式集載入資訊。

 

 

Microsoft.Extensions.Logging

我們對Microsoft.Extensions.Logging庫中的控制台日誌提供程式進行了改進。現在,您可以實現自定義ConsoleForMatter來完全控制控制台輸出的格式化和彩色化。格式化程式API通過實現VT-100(受大多數現代終端支援)轉義序列的子集來實現豐富的格式化。控制台記錄器可以解析出不支援的終端上的轉義序列,允許您為所有終端編寫一個格式化程式。

除了對訂製格式化程式的支援之外,我們還添加了一個內置的JSON格式化程式,它可以將結構化的JSON日誌發送到控制台。

 

轉儲調試

調試託管程式碼需要了解託管對象和構造。數據訪問組件(DAC)是運行時執行引擎的子集,它了解這些構造,可以在沒有運行時的情況下訪問這些託管對象。在Linux上收集的.Net Core進程轉儲現在可以在Windows上使用WinDBG或DotNet Dump Analyze進行分析。

我們還添加了對從MacOS上運行的.NET進程捕獲ELF轉儲的支援。由於ELF不是MacOS上的本機可執行文件格式(像lldb這樣的本機調試器不能處理這些轉儲),我們將其作為一種選擇加入的特性。要在MacOS上啟用轉儲收集支援,請設置環境變數COMPLUS_DbgEnableElfDumpOnMacOS=1。生成的轉儲可以使用DotNet Dump Analyze進行分析。

列印環境資訊

隨著.NET擴展了對新作業系統和晶片體系結構的支援,人們有時想要一種列印環境資訊的方式。我們創建了一個簡單的.NET工具來完成此任務,名為dotnet-runtimeinfo。

您可以使用以下命令安裝和運行該工具。

dotnet tool install -f dotnet-runtimeinfo
dotnet-runtimeinfo

該工具為您的環境生成以下形式的輸出。

**.NET information
Version: 5.0.0
FrameworkDescription: .NET 5.0.0
Libraries version: 5.0.0
Libraries hash: cf258a14b70ad9069470a108f13765e0e5988f51

**Environment information
OSDescription: Linux 5.8.6-1-MANJARO-ARM #1 SMP Thu Sep 3 22:01:08 CEST 2020
OSVersion: Unix 5.8.6.1
OSArchitecture: Arm64
ProcessorCount: 6

**CGroup info**
cfs_quota_us: -1
memory.limit_in_bytes: 9223372036854771712
memory.usage_in_bytes: 2740666368

運行時和庫
在運行時和庫中有很多改進
RyuJIT的程式碼品質改進
這個版本對JIT有很多改進,我在以前的.NET5.0預覽帖中分享了其中的許多改進。在這篇文章中,我提升了來自社區的改變。


  • 將xmm用於堆棧prolog-dotnet/Runtime#32538-更改為x86/x64 prolog零位調整程式碼。改進:JSON;TechEmpower。致謝:本·亞當斯。

  • 用於ARM64的Vectorise位數組-Dotnet/Runtime#33749-BitArray類已更新,包括使用ARM64內部結構的ARM64硬體加速實現。BitArray的性能改進非常顯著。感謝@Gnbrkm41。

  • 動態通用詞典擴展特性dotnet/運行時#32270-一些(可能是大多數?)。基於改進了運行庫用來存儲有關泛型類型和方法的資訊的低級(本機程式碼)字典的實現,泛型的使用現在有了更好的性能(最初的性能發現)。有關更多資訊,請參見Perf:COLLECTION COUNT()在Core中比CLR慢。錯誤報告歸功於@RealDotNetDave。

  • Implementate Vector.Celing/Vector.Floor Dotnet/Runtime#31993-按照API提案,使用x64和Arm64內部函數實現Vector.Celing/Vector.Floor。感謝@Gnbrkm41。

  • TailCall助手的新的、更快的、可移植的實現。來源:Jakob Botsch
    Nielsen(.NET團隊實習生)。來自@dsymetweet的反應。

  • 添加VectorTableList和TableVectorExtension內部-Credit:@TamarChristinaArm(ARM Holdings)。

  • 使用新的硬體內部組件BSF/BSR-Credit@saucecontrol提高英特爾架構性能。

  • 實現向量{Size}.AllBitsSet-Credit@Gnbrkm41。

  • 允許逃避一些邊界檢查-Credit@Nathan-Moore

垃圾收集器GC
在GC中進行了以下改進。


  • 卡片標記竊取-dotnet/coreclr#25986-伺服器GC(在不同執行緒上)現在可以工作竊取,同時標記由老一代對象持有的0/1類對象。這意味著,在某些GC執行緒標記時間比其他執行緒長得多的情況下,短暫的GC暫停會更短。

  • 引入固定對象堆-DotNet/Runtime#32283-添加固定對象堆(PoH)。這個新堆(大對象堆(LOH)的對等體)將允許GC單獨管理固定對象,從而避免固定對象對世代堆的負面影響。

  • 允許從空閑列表分配大對象,同時後台使用空閑列表清理啟用了SOH的LOH分配,而BGC正在清理SOH。以前,這隻在LOH上使用段末尾空間。這允許更好地使用堆。

  • 後台GC掛起修復-dotnet/coreclr#27729-掛起修復減少了bgc和用戶執行緒掛起的時間。這減少了在GC發生之前掛起託管執行緒所需的總時間。Dotnet/coreclr#27578也促成了同樣的結果。

  • 修正了擴展塢中的命名組組處理,增加了對從命名組組讀取限制的支援。以前我們只讀全球版本。

  • 優化矢量化排序-Dotnet/Runtime#37159-GC中的矢量化標記列表排序,減少了短暫的GC暫停時間(也包括Dotnet/Runtime#40613)。

  • 世代感知分析-DotNet/Runtime#40322-世代感知分析,允許您確定哪些老一代對象保留在年輕一代對象上,從而使它們存活下來,並導致短暫的GC暫停時間。

  • 優化分解GC堆記憶體頁面-Dotnet/Runtime#35896-優化分解,更好地分解邏輯,對於伺服器GC來說,完全退出了「停止世界」階段,從而減少了阻塞GC的暫停時間。

現在,GC通過EgGetGCMmemyInfo方法公開最新集合的詳細資訊。GCMmemyInfo結構提供有關機器記憶體、堆記憶體和您指定的GC類型的最新集合或最新集合的資訊-臨時GC、完全阻塞GC或後台GC。

使用這個新API的最有可能的用例是日誌記錄/監視,或者向載入器平衡器指示機器應該停止旋轉以請求完整的GC。它還可以通過減小快取大小來避免容器硬限制。

另一個小而有效的改變是將昂貴的重置記憶體操作推遲到記憶體不足的情況。我們預計策略中的這些更改將降低GC延遲(以及總體上的GC CPU使用率)。
Windows ARM64

現在,.NET應用程式可以在Windows
Arm64上本地運行。在此之前,我們在.NETCore3.0中添加了對Linux Arm64的支援(對Glibc和MUSL的支援)。使用.NET 5.0,您可以在Windows Arm64設備(如Surface
Pro X)上開發和運行應用程式。您已經可以在Windows Arm64上運行.NET Core和.NET Framework應用程式,但需要通過x86模擬。這是可行的,但是原生ARM64執行的性能要好得多。

針對ARM64的MSI安裝程式是此版本的最終更改之一。您可以在下圖中看到.NET5.0SDK安裝程式。

 

 

 

NET 5.0 SDK目前不包含Windows Arm64上的Windows桌面組件-Windows Forms和WPF。這一變化最初是在.NET5.0預覽版8中發布的。我們希望在5.0服務更新中添加Windows Arm64的Windows桌麵包。我們目前還沒有可以分享的日期。在此之前,Windows Arm64支援SDK、控制台和ASP.NET Core應用程式,但Windows桌面組件不支援。

ARM64性能

一年多來,我們在提高ARM64性能方面投入了大量資金。我們致力於使ARM64成為一個基於.NET的高性能平台。這些改進同樣適用於Windows和Linux。平台可移植性和一致性一直是.NET令人信服的特點。這包括無論您在哪裡使用.NET都能提供出色的性能。在.NET Core 3.x中,ARM64的功能與x64不相上下,但缺少一些關鍵的性能特性和投資。我們已經在.NET5.0中解決了這個問題,正如在.NET5.0中的ARM64性能中所描述的那樣。

改進之處在於:

  • l  調整ARM64的JIT優化(示例)。
  • l  啟用並利用ARM64硬體特性(示例)。
  • l  調整ARM64庫中的關鍵性能演算法(例如)。

有關更多詳細資訊,請參見在.NET5.0中提高Arm64性能。

硬體內部屬性是我們在.NET Core3.0中添加的一個低級性能特性。當時,我們增加了對x86-64指令和晶片的支援。作為.NET5.0的一部分,我們正在擴展該功能以支援ARM64。僅僅創建內部結構並不能提高性能。它們需要在性能關鍵型程式碼中使用。我們在.NET5.0的.NET庫中廣泛利用了Arm64的內部特性。您也可以在自己的程式碼中做到這一點,儘管您需要熟悉CPU指令才能做到這一點。

我將用一個類比來解釋硬體內部是如何工作的。在很大程度上,開發人員依賴於.NET中內置的類型和API,比如string.Split或HttpClient。這些API通常通過P/Invoke功能利用本地作業系統API。P/Invoke支援高性能的本機互操作,並在.NET庫中為此廣泛使用。您可以自己使用相同的功能來調用本機API。硬體內部功能類似,不同之處在於它們不是調用作業系統API,而是使您能夠在程式碼中直接使用CPU指令。它大致相當於C++內部函數的.NET版本。硬體本質最好被認為是一種CPU硬體加速功能。它們提供了非常實實在在的好處,現在是.NET庫性能基礎的關鍵部分,並負責您可以在.NET5.0性能帖子中讀到的許多好處。與C++相比,當.NET內部函數被AOT編譯成隨時可以運行的文件時,內部函數沒有運行時性能損失。

注意:Visual C++編譯器具有類似的內部特性。您可以直接將C++與.NET硬體內部功能進行比較,如果您在System.Rune me.Intrinsics.X86.Avx2、x64(AMD64)內部功能列表和英特爾內部功能指南中搜索_mm_i32ather_ep32,就可以看到這一點。你會看到很多相似之處。

我們在5.0中對ARM64的性能進行了第一次重大投資,並將在後續版本中繼續這方面的努力。我們直接與ARM Holdings的工程師合作,確定產品改進的優先順序,並設計最充分利用ARMv8
ISA的演算法。其中一些改進將為ARM32帶來價值,然而,我們並沒有將獨特的努力應用於ARM32。如果你使用的是Raspberry Pi,如果你安裝了新的Arm64版本的Raspberry Pi OS,你會享受到這些改進。

我們預計蘋果將在任何時候發布新的蘋果硅基Mac電腦。我們已經有了針對Apple Silicon的.NET6.0的早期版本,並一直在與蘋果的工程師合作,幫助為該平台優化.NET。我們在Apple Silicon(Credit@Sickler)上也有一些早期的社區參與。

P95+延遲
我們看到越來越多的大型面向互聯網的網站和服務託管在.NET上。雖然有很多合理的關注點放在每秒請求數(RPS)指標上,但我們發現沒有大的網站所有者問我們這個問題或要求數百萬的RPS。然而,我們聽說了很多關於延遲的事情,特別是關於改善P95或P99延遲的問題。通常,為站點配置的機器或核心的數量(以及最大的成本驅動因素)是根據達到特定的P95指標(而不是P50)來選擇的。我們認為延遲是真正的「金錢指標」。

我們在Stack Overflow的朋友們在分享他們服務上的數據方面做得很好。他們的一位工程師尼克·克雷弗(Nick Craver)最近分享了他們在遷移到.NET Core後看到的延遲改進:

鎖定對象一直是GC性能的長期挑戰,特別是因為它們加速(或導致)記憶體碎片。我們為固定對象添加了一個新的GC堆。固定對象堆基於這樣的假設,即進程中固定的對象非常少,但它們的存在會造成不成比例的性能挑戰。將固定對象(尤其是那些由.NET庫作為實現細節創建的對象)移動到一個獨特的區域是有意義的,這樣會使世代GC堆中只有很少的固定對象,甚至沒有固定對象,因此性能會大大提高。

最近,我們一直在應對大中華區長期存在的挑戰。DotNet/Runtime#2795應用了一種新的GC靜態掃描方法,在確定GC堆對象的活躍度時避免了鎖爭用。Dotnet/Runtime#25986使用了一種新演算法,用於在垃圾收集的標記階段跨核心平衡GC工作,這應該會增加大堆垃圾收集的吞吐量,進而減少延遲。

提高分層編譯的性能

我們一直致力於改進多版本的分層編譯。我們繼續將其視為一個關鍵的性能特性,無論是啟動性能還是穩態性能。在這個版本中,我們對分層編譯做了兩大改進。

分層編譯的主要機制是調用計數。一旦一個方法被調用n次,運行庫就會要求JIT以更高的品質重新編譯該方法。從我們最早的性能分析中,我們知道呼叫計數機制太慢,但沒有看到一個簡單的方法來解決這個問題。作為.NET5.0的一部分,我們改進了分層JIT編譯使用的調用計數機制,以平滑啟動時的性能。在過去的版本中,我們看到了在進程生命周期的前10-15秒(主要是Web伺服器)中出現的不可預測的性能報告。這個問題現在應該得到解決。

我們發現的另一個性能挑戰是對帶有循環的方法使用分層編譯。根本問題是,您可以使用一個循環多次的冷方法(只調用一次或幾次;$lt;n)。我們稱這種病態場景為「冷方法;熱循環」。很容易想像這種情況會發生在應用程式的Main方法中。因此,默認情況下,我們禁用了具有循環的方法的分層編譯。相反,我們允許應用程式選擇使用帶循環的分層編譯。PowerShell是在看到某些場景的高個位數性能改進後選擇這樣做的應用程式。

為了更好地處理具有循環的方法,我們實現了棧上替換(OSR)。這類似於Java虛擬機具有的同名功能。OSR使當前正在運行的方法執行的程式碼能夠在方法執行過程中重新編譯,而這些方法是活動的「堆棧上」。這一功能目前還處於試驗階段和選擇加入階段,並且僅在x64上。

要使用OSR,必須啟用多項功能。PowerShell項目文件是一個很好的起點。您會注意到,分層編譯和所有快速JIT功能都已啟用。此外,需要將COMPLUS_TC_OnStackReplace環境變數設置為1。

或者,您也可以設置以下兩個環境變數,假設所有其他設置都有其預設值:

COMPLUS_TC_QuickJitForLoops=1。
COMPLUS_TC_OnStackReplace=1。
我們不打算在.NET5.0中默認啟用OSR,也還沒有決定是否會在生產中支援它。

在Windows上支援ICU

我們使用ICU庫來支援Unicode和全球化,以前只在Linux和MacOS上使用。我們現在在Windows
10上使用相同的庫。這一更改使得全球化API的行為在Windows
10、MacOS和Linux之間保持一致,例如特定於區域性的字元串比較。我們還將ICU與Blazor WebAssembly配合使用。

將System.DirectoryServices.Protooles擴展到Linux和MacOS

我們一直在添加對System.DirectoryServices.Protooles的跨平台支援。這包括對Linux的支援和對MacOS的支援。Windows支援是預先存在的。

協議是一個比System.DirectoryServices更低級的API,可以支援(或可以用來支援)更多場景。System.DirectoryServices包含僅限Windows的概念/實現,因此它不是跨平台的明顯選擇。這兩個API集都支援控制目錄服務伺服器並與其交互,如LDAP或Active Directory。

System.Text.Json

System.Text.Json在.NET5.0中得到了顯著改進,以提高性能和可靠性,並使熟悉Newtonsoft.Json的人們更容易採用。它還支援將JSON對象反序列化為記錄。

如果您正在考慮使用System.Text.Json作為Newtonsoft.Json的替代方案,您應該查看遷移指南。本指南闡明了這兩個API之間的關係。Json旨在涵蓋許多與Newtonsoft.Json相同的場景,但它並不是要替代流行的JSON庫,也不是要實現與流行的JSON庫相同的功能。我們試圖在性能和可用性之間保持平衡,在我們的設計選擇中偏向於性能。

HttpClient擴展方法

JsonSerializer擴展方法現在在HttpClient上公開,極大地簡化了這兩個API的結合使用。這些擴展方法消除了複雜性,並為您處理了各種場景,包括處理內容流和驗證內容媒體類型。Steve Gordon很好地解釋了在System.Net.Http.Json中使用HttpClient發送和接收JSON的好處。

下面的示例將天氣預報JSON數據反序列化為Forecast記錄,使用新的

using System;
using System.Net.Http;
using System.Net.Http.Json;

string serviceURL = "//localhost:5001/WeatherForecast";
HttpClient client = new();
Forecast[] forecasts = await client.GetFromJsonAsync<Forecast[]>(serviceURL);

foreach(Forecast forecast in forecasts)
{
    Console.WriteLine($"{forecast.Date}; {forecast.TemperatureC}C; {forecast.Summary}");
}

// {"date":"2020-09-06T11:31:01.923395-07:00","temperatureC":-1,"temperatureF":31,"summary":"Scorching"}            
public record Forecast(DateTime Date, int TemperatureC, int TemperatureF, string Summary);

這段程式碼很緊湊!它依賴於C#9中的頂級程式和記錄以及新的GetFromJsonAsync<T>()擴展方法。在如此接近的情況下使用Foreach和Await可能會讓您懷疑,我們是否要添加對JSON對象流的支援。我真的希望如此。

您可以在自己的機器上嘗試此功能。以下.NET SDK命令將使用WebAPI模板創建天氣預報服務。默認情況下,它將在以下網址公開服務://localhost:5001/WeatherForecast.。這與示例中使用的URL相同。

rich@thundera ~ % dotnet new webapi -o webapi
rich@thundera ~ % cd webapi 
rich@thundera webapi % dotnet run

確保您已經運行了DotNet dev-certs https–首先信任,否則客戶端和伺服器之間的握手將不起作用。如果遇到問題,請參閱信任ASP.NET核心HTTPS開發證書。

然後,您可以運行上一個示例。

rich@thundera ~ % git clone //gist.github.com/3b41d7496f2d8533b2d88896bd31e764.git weather-forecast
rich@thundera ~ % cd weather-forecast
rich@thundera weather-forecast % dotnet run
9/9/2020 12:09:19 PM; 24C; Chilly
9/10/2020 12:09:19 PM; 54C; Mild
9/11/2020 12:09:19 PM; -2C; Hot
9/12/2020 12:09:19 PM; 24C; Cool
9/13/2020 12:09:19 PM; 45C; Balmy

改進了對不可變類型的支援
定義不可變類型有多種模式。記錄只是最新的記錄。JsonSerializer現在支援不可變類型。

在本例中,您將看到帶有不可變結構的序列化。

using System;
using System.Text.Json;
using System.Text.Json.Serialization;

var json = "{"date":"2020-09-06T11:31:01.923395-07:00","temperatureC":-1,"temperatureF":31,"summary":"Scorching"} ";           
var options = new JsonSerializerOptions()
{
    PropertyNameCaseInsensitive = true,
    IncludeFields = true,
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
var forecast = JsonSerializer.Deserialize<Forecast>(json, options);

Console.WriteLine(forecast.Date);
Console.WriteLine(forecast.TemperatureC);
Console.WriteLine(forecast.TemperatureF);
Console.WriteLine(forecast.Summary);

var roundTrippedJson = JsonSerializer.Serialize<Forecast>(forecast, options);

Console.WriteLine(roundTrippedJson);

public struct Forecast{
    public DateTime Date {get;}
    public int TemperatureC {get;}
    public int TemperatureF {get;}
    public string Summary {get;}
    [JsonConstructor]
    public Forecast(DateTime date, int temperatureC, int temperatureF, string summary) => (Date, TemperatureC, TemperatureF, Summary) = (date, temperatureC, temperatureF, summary);
}

  

注意:JsonConstructor屬性是指定要與結構一起使用的構造函數所必需的。對於類,如果只有一個構造函數,則不需要該屬性。記錄也是如此。

它會產生以下輸出:

 

rich@thundera jsonserializerimmutabletypes % dotnet run
9/6/2020 11:31:01 AM
-1
31
Scorching
{"date":"2020-09-06T11:31:01.923395-07:00","temperatureC":-1,"temperatureF":31,"summary":"Scorching"}

支援記錄
JsonSerializer對記錄的支援幾乎與我剛才向您展示的對不可變類型的支援相同。我想在這裡展示的不同之處在於將JSON對象反序列化為一個記錄,該記錄公開了一個參數化的構造函數和一個可選的init屬性。

以下是程式片段,包括Record定義:

using System;
using System.Text.Json;

Forecast forecast = new(DateTime.Now, 40)
{
    Summary = "Hot!"
};

string forecastJson = JsonSerializer.Serialize<Forecast>(forecast);
Console.WriteLine(forecastJson);
Forecast? forecastObj = JsonSerializer.Deserialize<Forecast>(forecastJson);
Console.Write(forecastObj);

public record Forecast (DateTime Date, int TemperatureC)
{
    public string? Summary {get; init;}
};

它會產生以下輸出:

rich@thundera jsonserializerrecords % dotnet run
{"Date":"2020-09-12T18:24:47.053821-07:00","TemperatureC":40,"Summary":"Hot!"}
Forecast { Date = 9/12/2020 6:24:47 PM, TemperatureC = 40, Summary = Hot! }

改進了對Dictionary<K,V>的支援

JsonSerializer現在支援使用非字元串鍵的字典。您可以在下面的示例中看到這是什麼樣子。在.NET Core 3.0中,此程式碼進行編譯,但會引發NotSupportdException異常。

using System;
using System.Collections.Generic;
using System.Text.Json;

Dictionary<int, string> numbers = new ()
{
    {0, "zero"},
    {1, "one"},
    {2, "two"},
    {3, "three"},
    {5, "five"},
    {8, "eight"},
    {13, "thirteen"},
    {21, "twenty one"},
    {34, "thirty four"},
    {55, "fifty five"},
};

var json = JsonSerializer.Serialize<Dictionary<int, string>>(numbers);

Console.WriteLine(json);

var dictionary = JsonSerializer.Deserialize<Dictionary<int, string>>(json);

Console.WriteLine(dictionary[55]);

它會產生以下輸出。

rich@thundera jsondictionarykeys % dotnet run
{"0":"zero","1":"one","2":"two","3":"three","5":"five","8":"eight","13":"thirteen","21":"twenty one","34":"thirty four","55":"fifty five"}
fifty five

對欄位的支援

JsonSerializer現在支援欄位。這一變化是由@YohDeadfall貢獻的。謝謝!。

您可以在下面的示例中看到這是什麼樣子。在.NET Core
3.0中,JsonSerializer無法序列化或反序列化使用欄位的類型。對於具有欄位且無法更改的現有類型,這是一個問題。有了這一變化,這就不再是問題了。

using System;
using System.Text.Json;

var json = "{\"date\":\"2020-09-06T11:31:01.923395-07:00\",\"temperatureC\":-1,\"temperatureF\":31,\"summary\":\"Scorching\"} ";           
var options = new JsonSerializerOptions()
{
    PropertyNameCaseInsensitive = true,
    IncludeFields = true,
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
var forecast = JsonSerializer.Deserialize<Forecast>(json, options);

Console.WriteLine(forecast.Date);
Console.WriteLine(forecast.TemperatureC);
Console.WriteLine(forecast.TemperatureF);
Console.WriteLine(forecast.Summary);

var roundTrippedJson = JsonSerializer.Serialize<Forecast>(forecast, options);

Console.WriteLine(roundTrippedJson);

public class Forecast{
    public DateTime Date;
    public int TemperatureC;
    public int TemperatureF;
    public string Summary;
}

它會產生以下輸出。

rich@thundera jsonserializerfields % dotnet run
9/6/2020 11:31:01 AM
-1
31
Scorching
{"date":"2020-09-06T11:31:01.923395-07:00","temperatureC":-1,"temperatureF":31,"summary":"Scorching"}

保留JSON對象圖中的引用

JsonSerializer增加了對在JSON對象圖中保留(循環)引用的支援。它通過存儲ID來實現這一點,當JSON字元串被反序列化為對象時,這些ID可以重新組成。

using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;

Employee janeEmployee = new()
{
    Name = "Jane Doe",
    YearsEmployed = 10
};

Employee johnEmployee = new()
{
    Name = "John Smith"
};

janeEmployee.Reports = new List<Employee> { johnEmployee };
johnEmployee.Manager = janeEmployee;

JsonSerializerOptions options = new()
{
    // NEW: globally ignore default values when writing null or default
    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault,
    // NEW: globally allow reading and writing numbers as JSON strings
    NumberHandling = JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString,
    // NEW: globally support preserving object references when (de)serializing
    ReferenceHandler = ReferenceHandler.Preserve,
    IncludeFields = true, // NEW: globally include fields for (de)serialization
    WriteIndented = true,};

string serialized = JsonSerializer.Serialize(janeEmployee, options);
Console.WriteLine($"Jane serialized: {serialized}");

Employee janeDeserialized = JsonSerializer.Deserialize<Employee>(serialized, options);
Console.Write("Whether Jane's first report's manager is Jane: ");
Console.WriteLine(janeDeserialized.Reports[0].Manager == janeDeserialized);

public class Employee
{
    // NEW: Allows use of non-public property accessor.
    // Can also be used to include fields "per-field", rather than globally with JsonSerializerOptions.
    [JsonInclude]
    public string Name { get; internal set; }

    public Employee Manager { get; set; }

    public List<Employee> Reports;

    public int YearsEmployed { get; set; }

    // NEW: Always include when (de)serializing regardless of global options
    [JsonIgnore(Condition = JsonIgnoreCondition.Never)]
    public bool IsManager => Reports?.Count > 0;
}

性能

在.NET5.0中,JsonSerializer的性能得到了顯著提高。Stephen Toub在他的.NET5帖子中談到了JsonSerializer的一些性能改進。我還在.NET5.0RC1文章中更詳細地介紹了Json的性能。

應用程式部署

在編寫或更新應用程式之後,您需要部署它以使您的用戶受益。這可能是到Web伺服器、雲服務或客戶端電腦,也可能是使用Azure DevOps或GitHub操作等服務的CI/CD流的結果。

我們努力提供一流的部署功能,自然地與應用程式類型保持一致。對於.NET5.0,我們專註於改進單文件應用程式,減少停靠多階段構建的容器大小,並為使用.NET Core部署ClickOnce應用程式提供更好的支援。

容器

我們認為容器是最重要的雲趨勢,並在這方面投入了大量資金。我們正在以多種方式投資容器,在.NET軟體堆棧的多個級別上。首先是我們對基本面的投資,這越來越多地受到容器場景和部署容器應用的開發者的影響。

我們正在讓與集裝箱管弦樂團的合作變得更容易。我們已經添加了OpenTelemeter支援,這樣您就可以從您的應用程式中捕獲分散式跟蹤和指標。DotNet-monitor是一種新工具,旨在作為從.NET進程訪問診斷資訊的主要方式。特別是,我們已經開始構建dotnet-monitor的容器變體,您可以將其用作應用程式側車。最後,我們正在構建DotNet/Tye,以此來提高微服務開發人員的工作效率,包括開發和部署到Kubernetes環境。

NET運行時現在支援cgroup v2,我們預計它將在2020年後成為與容器相關的重要API。Docker目前使用的是cgroup v1(已經被.NET支援)。相比之下,cgroup v2比cgroup v1更簡單、更高效、更安全。您可以通過我們2019年Docker更新了解更多關於cgroup和Docker資源限制的資訊。Linux發行版和容器運行時正在添加對cgroup v2的支援。一旦cgroup v2環境變得更加普遍,.Net 5.0將在cgroup v2環境中正常工作。這歸功於Omair Majid,他在Red Hat支援.NET。

除了Nano Server,我們現在還發布Windows Server Core鏡像。我們添加了Server Core,因為我們收到了客戶的回饋,他們想要一個與Windows Server完全兼容的.NET映像。如果你需要這個,那麼這張新照片就是為你準備的。支援Windows Server 2019長期服務渠道(LTSC)、.NET5.0和x64的組合。我們還進行了其他更改,以減小Windows伺服器核心映像的大小。這些改進帶來了很大的不同,但都是在Windows
Server 2019發布之後做出的。然而,它們將使下一個Windows Server LTSC版本受益。

作為使用「.NET」作為產品名稱的一部分,我們現在將.NET Core 2.1、3.1和.NET5.0鏡像發布到mcr.microsoft.com/dotnet系列的Repos中,而不是發布到mcr.microsoft.com/dotnet/core。我們將繼續將.NET
Core 2.1和3.1雙重發布到以前的位置,同時支援這些版本。.Net 5.0影像將僅發布到新位置。請相應地更新您的From語句和腳本。

作為.NET5.0的一部分,我們將SDK鏡像重新建立在ASP.NET鏡像之上,而不是構建包-dep,以顯著減小您在多階段構建場景中拉取的聚合鏡像的大小。

此更改對於多階段構建有以下好處,其中包含一個示例Dockerfile:

Ubuntu 20.04 Focus的多階段構建成本:

Pull Image

Before

After

sdk:5.0-focal

268 MB

232 MB

aspnet:5.0-focal

64 MB

10 KB (manifest
only)

減少了約: 100 MB (-30%)

Debian 10
Buster的多階段構建成本:

Pull Image

Before

After

sdk:5.0

280 MB

218 MB

aspnet:5.0

84 MB

4 KB (manifest
only)

減少了約: 146 MB (-40%)

有關更多詳細資訊,請參見Dotnet/Dotnet-docker#1814。

此更改有助於多階段構建,其中SDK和您的目標aspnet或運行時鏡像的版本相同(我們預計這是常見的情況)。在進行此更改時,(例如)aspnet拉入將是不可行的,因為您將通過最初的SDK拉入拉出aspnet層。

我們對阿爾卑斯和Nano伺服器做了類似的更改。阿爾卑斯和Nano伺服器都沒有Buildpack-dep鏡像。但是,阿爾卑斯和Nano Server的SDK鏡像之前並不是在ASP.NET鏡像之上構建的。我們解決了這個問題。你將會看到阿爾卑斯和Nano伺服器以及5.0版本在多階段構建方面都獲得了巨大的成功。

單文件應用程式

單個文件應用程式作為單個文件發布和部署。該應用程式及其依賴項都包含在該文件中。當應用程式運行時,依賴項直接從該文件載入到記憶體中(不會影響性能)。

在.NET5.0中,單文件應用程式主要集中在Linux上(稍後會詳細介紹)。它們可以是依賴於框架的,也可以是獨立的。依賴於全球安裝的.NET運行時,依賴於框架的單個文件應用程式可能非常小。自包含的單文件應用程式較大(由於帶有運行庫),但不需要在安裝前安裝.NET運行庫,因此可以直接運行。一般來說,依賴於框架對開發和企業環境都有好處,而對於ISV來說,自包含通常是更好的選擇。

我們用.NET Core 3.1製作了一個版本的單文件應用程式。它將二進位文件打包到單個文件中進行部署,然後將這些文件解壓縮到一個臨時目錄中以載入和執行它們。在某些情況下,這種方法可能會更好,但我們希望我們為5.0構建的解決方案會更好,這是一個值得歡迎的改進。

要創建真正的單一文件解決方案,我們需要克服多個障礙。關鍵任務是創建一個更複雜的應用程式捆綁器,並教導運行庫從二進位資源載入程式集。我們還遇到了一些無法逾越的障礙。

在所有平台上,我們都有一個稱為「apphost」的組件。這是成為可執行文件的文件,例如Windows上的myapp.exe或基於Unix的平台上的./myapp。對於單一文件應用程式,我們創建了一個新的apphost,我們稱之為「超級主機」。它具有與常規apphost相同的角色,但還包括運行時的靜態鏈接副本。超級主機是我們單一文件方法的一個基本設計點。這個模型就是我們在帶有.NET5.0的Linux上使用的模型。由於各種作業系統的限制,我們無法在Windows或MacOS上實現這種方法。我們在Windows或MacOS上沒有超級主機。在這些作業系統上,本地運行時二進位文件(大約3個)位於單個文件應用程式旁邊(導致「不是單個文件」)。我們將在.NET6.0中重新討論這種情況,然而,我們預計我們遇到的問題仍然具有挑戰性。

您可以使用以下命令來生成單文件應用程式。

l  依賴框架的單文件APP:

n  DotNet PUBLISH-r Linux-x64–自含式FALSE/p:PublishSingleFile=TRUE。

l  自含式單文件APP:

n  DotNet PUBLISH-r Linux-x64–自含式TRUE/p:PublishSingleFile=TRUE

 

您還可以使用項目文件配置單個文件發布。

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net5.0</TargetFramework>
    <!-- The OS and CPU type you are targeting -->
    <RuntimeIdentifier>linux-x64</RuntimeIdentifier>
    <!-- Determine self-contained or framework-dependent -->
    <SelfContained>true</SelfContained>
    <!-- Enable single file -->
    <PublishSingleFile>true</PublishSingleFile>
  </PropertyGroup>

</Project>

您可以嘗試使用程式集修剪來減小應用程式的大小。它可能會通過過度修剪來破壞應用程式,因此建議在使用此功能後徹底測試您的應用程式。程式集調整還會移除提前編譯的讀到運行本機程式碼(用於被調整的程式集),這主要是為了提高性能。您需要在裁剪之後測試應用程式的性能。通過使用PublishReadyToRun屬性(並設置為true),您可以在裁剪後即可運行編譯您的應用程式。

註:

  • l  應用程式是特定於作業系統和架構的。您需要發布每種配置(Linuxx64、LinuxArm64、Windowsx64、…)。。
  • l  配置文件(如*.runtimeconfig.json)包含在單個文件中。如果需要,您可以在單個文件旁邊放置額外的配置文件(可能是為了測試)。
  • l  默認情況下,單個文件中不包括.pdb文件。您可以使用<DebugType>Embed</DebugType>屬性啟用PDB嵌入。
  • l  IncludeNativeLibrariesForSelfExtract屬性可用於在Windows和MacOS上嵌入本機運行時二進位文件,但它們必須在運行時解壓縮到臨時存儲中。不建議在一般情況下使用此功能。

ClickOnce

多年來,ClickOnce一直是流行的.NET部署選項。現在,它被.NET Core 3.1和.NET5.0
Windows應用程式支援。當我們將Windows窗體和WPF支援添加到.NET Core 3.0中時,我們知道很多人會希望使用ClickOnce進行應用程式部署。在過去的一年裡,.NET和Visual Studio團隊共同努力,在命令行和Visual Studio中啟用ClickOnce發布。

從項目一開始,我們就有兩個目標:

  • l  在Visual Studio中為ClickOnce啟用熟悉的體驗。
  • l  使用MSBuild或MAGE工具,通過命令行流為ClickOnce發布啟用現代CI/CD。
  • 用圖片向你展示這種體驗是最容易的。

讓我們從Visual Studio體驗開始,它以項目發布為中心。

 

 

我們目前支援的主要部署模式是依賴於框架的應用程式。很容易依賴於.NET桌面運行時(即包含WPF和Windows窗體的桌面運行時)。如果需要,ClickOnce安裝程式將在用戶電腦上安裝.NET運行時。我們還打算支援獨立和單一文件應用程式。

 

 

 

您可能會想,您是否仍然能夠利用ClickOnce離線和更新功能。可以,停那兒吧。

 

 

 

MAGE最大的變化是它現在是一個.NET工具,發布在NuGet上。這意味著你不需要在你的機器上安裝任何特殊的東西。您只需要.NET5.0SDK,然後就可以將MAGE安裝為一個.NET工具。您也可以使用它發布.NET Framework應用程式,但是,SHA1簽名和部分信任支援已被移除。

MAGE安裝命令如下:

dotnet tool install -g Microsoft.DotNet.Mage

在您製作並分發了ClickOnce安裝程式之後,您的用戶將看到熟悉的ClickOnce安裝對話框。

 

 

 

當您使更新可用時,您的用戶將看到更新對話框。

 

 

 

最後總結

Net 5.0是另一個大版本,它應該會改進你使用.NET的許多方面。我們已經實現了一系列的改進,從單文件應用程式到性能,從Json序列化的可用性到ARM64的支援。雖然今天可能是您使用.NET5.0的第一天,但我們在微軟的產品中運行.NET5.0已經有幾個月了。我們相信,它已準備好供您使用、運營您的業務並為您的應用程式提供動力。C#9和F#5中的新語言改進應該會使您的程式碼更具表現力,更易於編寫。對於您現有的應用程式來說,.Net 5.0也是一個很好的選擇。在許多情況下,您可以毫不費力地升級。

如果您對性能感興趣,您可能會對我們在TechEmpower基準測試方面的進展感興趣。回過頭來看,您可以看到.NETCore3.1在最新一輪第19輪中的表現相當不錯。我們期待著在即將到來的第20輪中看到.NET5.0。當第20輪最終確定並發布時,新的排名將是值得關注的。

在.NET5.0中的改進是許多人共同努力的結果,他們在GitHub上,在世界各地,在多個時區協同工作。感謝為這一版本做出貢獻的每一個人。別擔心,有很多機會可以貢獻自己的力量。NET5.0版本已經結束,但是下一個版本已經開始了。

Net 5.0是另一個大版本,它應該會改進你使用.NET的許多方面。我們已經實現了一系列的改進,從單文件應用程式到性能,從Json序列化的可用性到ARM64的支援。雖然今天可能是您使用.NET5.0的第一天,但我們在微軟的產品中運行.NET5.0已經有幾個月了。我們相信,它已準備好供您使用、運營您的業務並為您的應用程式提供動力。C#9和F#5中的新語言改進應該會使您的程式碼更具表現力,更易於編寫。對於您現有的應用程式來說,.Net 5.0也是一個很好的選擇。在許多情況下,您可以毫不費力地升級。

如果您對性能感興趣,您可能會對我們在TechEmpower基準測試方面的進展感興趣。回過頭來看,您可以看到.NETCore3.1在最新一輪第19輪中的表現相當不錯。我們期待著在即將到來的第20輪中看到.NET5.0。當第20輪最終確定並發布時,新的排名將是值得關注的。

在.NET5.0中的改進是許多人共同努力的結果,他們在GitHub上,在世界各地,在多個時區協同工作。感謝為這一版本做出貢獻的每一個人。別擔心,有很多機會可以貢獻自己的力量。NET5.0版本已經結束,但是下一個版本已經開始了。