1. 程式人生 > >.NET 5.0正式釋出,功能特性介紹(翻譯)

.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。

  • l  安裝程式和二進位制檔案
  • l  Docker 容器 images
  • l  Linux package
  • l  發行說明文件
  • l  已知的問題
  • l  GitHub問題跟蹤器
  • l  .NET 5.0貢獻者

對於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中有許多重要的改進:

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

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

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

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

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

l  通過ClickOnce客戶端釋出應用程式,單檔案應用程式,減小的容器映像大小以及新增的Server Core容器映像,應用程式部署選項更好。

l  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  不受管理的出口。
  • 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預覽帖中分享了其中的許多改進。在這篇文章中,我提升了來自社群的改變。

  • l  將xmm用於堆疊prolog-dotnet/Runtime#32538-更改為x86/x64 prolog零位調整程式碼。改進:JSON;TechEmpower。致謝:本·亞當斯。
  • l  用於ARM64的Vectorise位陣列-Dotnet/Runtime#33749-BitArray類已更新,包括使用ARM64內部結構的ARM64硬體加速實現。BitArray的效能改進非常顯著。感謝@Gnbrkm41。
  • l  動態通用詞典擴充套件特性dotnet/執行時#32270-一些(可能是大多數?)。基於改進了執行庫用來儲存有關泛型型別和方法的資訊的低階(本機程式碼)字典的實現,泛型的使用現在有了更好的效能(最初的效能發現)。有關更多資訊,請參見Perf:COLLECTION COUNT()在Core中比CLR慢。錯誤報告歸功於@RealDotNetDave。
  • l  Implementate Vector.Celing/Vector.Floor Dotnet/Runtime#31993-按照API提案,使用x64和Arm64內部函式實現Vector.Celing/Vector.Floor。感謝@Gnbrkm41。
  • l  TailCall助手的新的、更快的、可移植的實現。來源:Jakob Botsch Nielsen(.NET團隊實習生)。來自@dsymetweet的反應。
  • l  新增VectorTableList和TableVectorExtension內部-Credit:@TamarChristinaArm(ARM Holdings)。
  • l  使用新的硬體內部元件BSF/BSR-Credit@saucecontrol提高英特爾架構效能。
  • l  實現向量{Size}.AllBitsSet-Credit@Gnbrkm41。
  • l  允許逃避一些邊界檢查-Credit@Nathan-Moore

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

  • l  卡片標記竊取-dotnet/coreclr#25986-伺服器GC(在不同執行緒上)現在可以工作竊取,同時標記由老一代物件持有的0/1類物件。這意味著,在某些GC執行緒標記時間比其他執行緒長得多的情況下,短暫的GC暫停會更短。
  • l  引入固定物件堆-DotNet/Runtime#32283-新增固定物件堆(PoH)。這個新堆(大物件堆(LOH)的對等體)將允許GC單獨管理固定物件,從而避免固定物件對世代堆的負面影響。
  • l  允許從空閒列表分配大物件,同時後臺使用空閒列表清理啟用了SOH的LOH分配,而BGC正在清理SOH。以前,這隻在LOH上使用段末尾空間。這允許更好地使用堆。
  • l  後臺GC掛起修復-dotnet/coreclr#27729-掛起修復減少了bgc和使用者執行緒掛起的時間。這減少了在GC發生之前掛起託管執行緒所需的總時間。Dotnet/coreclr#27578也促成了同樣的結果。
  • l  修正了擴充套件塢中的命名組組處理,增加了對從命名組組讀取限制的支援。以前我們只讀全球版本。
  • l  優化向量化排序-Dotnet/Runtime#37159-GC中的向量化標記列表排序,減少了短暫的GC暫停時間(也包括Dotnet/Runtime#40613)。
  • l  世代感知分析-DotNet/Runtime#40322-世代感知分析,允許您確定哪些老一代物件保留在年輕一代物件上,從而使它們存活下來,並導致短暫的GC暫停時間。
  • l  優化分解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 = "https://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模板建立天氣預報服務。預設情況下,它將在以下網址公開服務:https://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 https://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在最