.NET 6 史上最全攻略(.net 6 preview 7)
歡迎使用.NET 6。今天的版本是.NET 團隊和社區(qū)一年多努力的結果。C# 10 和F# 6 提供了語言改進,使您的代碼更簡單、更好。性能大幅提升,我們已經看到微軟降低了托管云服務的成本。.NET 6 是第一個原生支持Apple Silicon (Arm64) 的版本,并且還針對Windows Arm64 進行了改進。我們構建了一個新的動態(tài)配置文件引導優(yōu)化(PGO) 系統(tǒng),該系統(tǒng)可提供僅在運行時才可能進行的深度優(yōu)化。使用DOTNET monitor和OpenTelemetry改進了云診斷。WebAssembly支持更有能力和性能。為HTTP/3添加了新的API ,處理JSON、數學和直接操作內存。.NET 6 將支持三年。開發(fā)人員已經開始將應用程序升級到.NET 6,我們在生產中聽到了很好的早期成果。.NET 6 已為您的應用程序做好準備。
您可以下載適用于Linux、macOS 和Windows 的.NET 6 。
- 安裝程序和二進制文件
- 容器圖像
- Linux 軟件包
- 發(fā)行說明
- API 差異
- 已知的問題
- gitHub 問題跟蹤器
請參閱ASP.NET Core、Entity Framework、Windows Forms、.NET MAUI、YARP和dotnet 監(jiān)視器帖子,了解各種場景中的新增功能。
.NET 6 亮點
.NET 6 是:
- 使用Microsoft 服務、其他公司運行的云應用程序和開源項目進行生產壓力測試 。
- 作為最新的長期支持(LTS) 版本支持三年 。
- 跨瀏覽器、云、桌面、物聯(lián)網和移動應用程序的統(tǒng)一平臺 ,所有這些都使用相同的.NET 庫并能夠輕松共享代碼。
- 性能 得到了極大的提高,尤其是文件I/O,這共同導致了執(zhí)行時間、延遲和內存使用的減少。
- C# 10提供了語言改進,例如記錄結構、隱式使用和新的lambda 功能,而編譯器添加了增量源生成器。 F# 6 添加了新功能,包括基于任務的異步、管道調試和眾多性能改進。
- Visual Basic 在Visual Studio 體驗和Windows 窗體項目打開體驗方面有所改進。
- Hot Reload 使您能夠跳過重新構建和重新啟動您的應用程序以查看新更改(在您的應用程序運行時),Visual Studio 2022 和.NET CLI 支持C# 和Visual Basic。
- 云診斷 已通過OpenTelemetry和dotnet monitor得到改進,現(xiàn)在在生產中支持并在Azure 應用服務中可用。
- JSON API 的功能更強大,并且通過序列化程序的源生成器具有更高的性能。
- ASP.NET Core中引入的 最小 API可簡化入門體驗并提高HTTP 服務的性能。
- Blazor組件現(xiàn)在可以從JavaScript 呈現(xiàn)并與現(xiàn)有的基于JavaScript 的應用程序集成。
- Blazor WebAssembly (Wasm) 應用程序的WebAssembly AOT 編譯,以及對運行時重新鏈接和本機依賴項的支持。
- 使用 http://ASP.NET Core 構建的單頁應用程序現(xiàn)在使用更靈活的模式,可與Angular、React 和其他流行的前端 JavaScript 框架一起使用。
- 添加了 HTTP/3,以便http://ASP.NET Core、HttpClient 和gRPC 都可以與HTTP/3 客戶端和服務器交互。
- File IO 現(xiàn)在支持符號鏈接,并且通過re-written-from-scratch 大大提高了性能FileStream。
- 通過支持OpenSSL 3、ChaCha20Poly1305 加密方案和運行時深度防御緩解,特別是W^X和CET ,安全 性得到了改進。
- 可以為Linux、macOS 和Windows(以前只有Linux)發(fā)布 單文件應用程序(免提?。?。
- IL 修整 現(xiàn)在更加強大和有效,新的警告和分析器可確保正確的最終結果。
- 添加了源生成器和分析器 ,可幫助您生成更好、更安全和更高性能的代碼。
- 源代碼構建 使Red Hat 等組織能夠從源代碼構建.NET 并向其用戶提供自己的構建。
該版本包括大約一萬次git 提交。即使這篇文章很長,它也跳過了許多改進。您必須下載并試用.NET 6 才能看到所有新功能。
支持
.NET 6 是一個長期支持(LTS) 版本,將支持三年。它支持多種操作系統(tǒng),包括macOS Apple Silicon 和Windows Arm64。
Red Hat與.NET 團隊合作,在Red Hat Enterprise Linux 上支持.NET。在RHEL 8 及更高版本上,.NET 6 將可用于AMD 和Intel (x64_64)、ARM (aarch64) 以及IBM Z 和LinuxONE (s390x) 架構。
請開始將您的應用程序遷移到.NET 6,尤其是.NET 5 應用程序。我們從早期采用者那里聽說,從.NET Core 3.1 和.NET 5 升級到.NET 6 很簡單。
Visual Studio 2022和Visual StudIO 2022 for Mac支持.NET 6 。Visual Studio 2019、Visual Studio for Mac 8 或MSBuild 16 不支持它。如果要使用.NET 6,則需要升級到Visual Studio 2022(現(xiàn)在也是64 位)。Visual Studio Code C# 擴展支持.NET 6 。
Azure App 服務:
- Azure Functions現(xiàn)在支持在.NET 6 中運行無服務器函數。
- App Service .NET 6 GA Announcement為ASP.NET Core 開發(fā)人員提供了信息和詳細信息,他們很高興今天開始使用.NET 6。
- Azure 靜態(tài)Web 應用現(xiàn)在支持帶有Blazor WebAssembly 前端和Azure Function API 的全棧.NET 6 應用程序。
注意:如果您的應用已經在應用服務上運行.NET 6 預覽版或RC 版本,則在將.NET 6 運行時和SDK 部署到您所在區(qū)域后,它將在第一次重新啟動時自動更新。如果您部署了一個獨立的應用程序,您將需要重新構建和重新部署。
統(tǒng)一擴展平臺
.NET 6 為瀏覽器、云、桌面、物聯(lián)網和移動應用程序提供了一個統(tǒng)一的平臺。底層平臺已更新,可滿足所有應用類型的需求,并便于在所有應用中重用代碼。新功能和改進同時適用于所有應用程序,因此您在云或移動設備上運行的代碼的行為方式相同并具有相同的優(yōu)勢。
.NET 開發(fā)人員的范圍隨著每個版本的發(fā)布而不斷擴大。機器學習和WebAssembly是最近添加的兩個。例如,通過機器學習,您可以編寫在流數據中查找異常的應用程序。使用WebAssembly,您可以在瀏覽器中托管.NET 應用程序,就像HTML 和JavaScript 一樣,或者將它們與HTML 和JavaScript 混合使用。
最令人興奮的新增功能之一是.NET Multi-platform App UI (.NET MAUI)。您現(xiàn)在可以在單個項目中編寫代碼,從而跨桌面和移動操作系統(tǒng)提供現(xiàn)代客戶端應用程序體驗。.NET MAUI 將比.NET 6 稍晚發(fā)布。我們在.NET MAUI 上投入了大量時間和精力,很高興能夠發(fā)布它并看到.NET MAUI 應用程序投入生產。
當然,.NET 應用程序也可以在家中使用Windows 桌面(使用Windows Forms和WPF)以及使用ASP.NET Core 在云中。它們是我們提供時間最長的應用程序類型,并且仍然非常受歡迎,我們在.NET 6 中對其進行了改進。
面向 .NET 6
繼續(xù)以廣泛平臺為主題,在所有這些操作系統(tǒng)上編寫.NET 代碼很容易。
要以 .NET 6 為目標,您需要使用.NET 6 目標框架,如下所示:
<TargetFramework>net6.0<TargetFramework>
net6.0 Target Framework Moniker (TFM) 使您可以訪問.NET 提供的所有跨平臺API。如果您正在編寫控制臺應用程序、http://ASP.NET Core 應用程序或可重用的跨平臺庫,這是最佳選擇。
如果您針對特定操作系統(tǒng)(例如編寫Windows 窗體或iOS 應用程序),那么還有另一組TFM(每個都針對不言而喻的操作系統(tǒng))供您使用。它們使您可以訪問所有net6.0的API以及一堆特定于操作系統(tǒng)的API。
- net6.0-android
- net6.0-ios
- net6.0-maccatalyst
- net6.0-tvos
- net6.0-windows
每個無版本TFM 都相當于針對.NET 6 支持的最低操作系統(tǒng)版本。如果您想要具體或訪問更新的API,可以指定操作系統(tǒng)版本。
net6.0和net6.0-windows TFMs 都支持(與.NET 5 相同)。Android 和Apple TFM 是.NET 6 的新功能,目前處于預覽階段。稍后的.NET 6 更新將支持它們。
操作系統(tǒng)特定的 TFM 之間沒有兼容性關系。 例如,net6.0-ios與 net6.0-tvos不兼容。 如果您想共享代碼,您需要使用帶有#if 語句的源代碼或帶有net6.0目標代碼的二進制文件來實現(xiàn)。
性能
自從我們啟動.NET Core 項目以來,該團隊一直在不斷地關注性能。Stephen Toub在記錄每個版本的.NET 性能進展方面做得非常出色。歡迎查看在.NET 6 中的性能改進的帖子。在這篇文章中,里面包括您想了解的重大性能改進,包括文件IO、接口轉換、PGO 和System.Text.Json。
動態(tài) PGO
動態(tài)輪廓引導優(yōu)化(PGO)可以顯著提高穩(wěn)態(tài)性能。例如,PGO 為TechEmpower JSON"MVC"套件的每秒請求數提高了26%(510K -> 640K)。
動態(tài)PGO 建立在分層編譯的基礎上,它使方法能夠首先非??焖俚鼐幾g(稱為"第0 層")以提高啟動性能,然后在啟用大量優(yōu)化的情況下隨后重新編譯(稱為"第1 層")一旦該方法被證明是有影響的。該模型使方法能夠在第0 層中進行檢測,以允許對代碼的執(zhí)行進行各種觀察。在第1 層重新調整這些方法時,從第0 層執(zhí)行收集的信息用于更好地優(yōu)化第1 層代碼。這就是機制的本質。
動態(tài)PGO 的啟動時間將比默認運行時稍慢,因為在第0 層方法中運行了額外的代碼來觀察方法行為。
要啟用動態(tài) PGO,請在應用程序將運行的環(huán)境中設置 DOTNET_TieredPGO=1。 您還必須確保啟用分層編譯(默認情況下)。 動態(tài) PGO 是可選的,因為它是一種新的且有影響力的技術。 我們希望發(fā)布選擇加入使用和相關反饋,以確保它經過全面壓力測試。 我們對分層編譯做了同樣的事情。 至少一個非常大的 Microsoft 服務支持并已在生產中使用動態(tài) PGO。 我們鼓勵您嘗試一下。
您可以在.NET 6中的性能帖子中看到更多關于動態(tài)PGO 優(yōu)勢的信息,包括以下微基準,它測量特定LINQ 枚舉器的成本。
private IEnumerator<long> _source = Enumerable.Range(0, long.MaxValue).GetEnumerator();[Benchmark]public void MoveNext() => _source.MoveNext();
這是有和沒有動態(tài)PGO 的結果。
這是一個相當大的差異,但代碼大小也有所增加,這可能會讓一些讀者感到驚訝。這是由JIT 生成的匯編代碼的大小,而不是內存分配(這是一個更常見的焦點)。.NET 6 性能帖子對此有很好的解釋。
PGO 實現(xiàn)中常見的一種優(yōu)化是"熱/冷分離",其中經常執(zhí)行的方法部分(“熱”)在方法開始時靠近在一起,而不經常執(zhí)行的方法部分(“冷”)是移到方法的末尾。這樣可以更好地使用指令緩存,并最大限度地減少可能未使用的代碼負載。
作為上下文,接口調度是 .NET 中最昂貴的調用類型。 非虛擬方法調用是最快的,甚至更快的是可以通過內聯(lián)消除的調用。 在這種情況下,動態(tài) PGO 為 MoveNext 提供了兩個(替代)調用站點。 第一個 – 熱的 – 是對 Enumerable RangeIterator.MoveNext的直接調用,另一個 – 冷的 – 是通過 IEnumerator<int>的虛擬接口調用。 如果大多數時候最熱門的人都被叫到,那將是一個巨大的勝利。
這就是魔法。當 JIT 檢測此方法的第 0 層代碼時,包括檢測此接口調度以跟蹤每次調用時 _source的具體類型。 JIT 發(fā)現(xiàn)每次調用都在一個名為 Enumerable RangeIterator的類型上,這是一個私有類,用于在 Enumerable實現(xiàn)內部實現(xiàn) Enumerable.Range。因此,對于第 1 層,JIT 已發(fā)出檢查以查看 _source的類型是否為 Enumerable RangeIterator:如果不是,則跳轉到我們之前強調的執(zhí)行正常接口調度的冷部分。但如果是 – 基于分析數據,預計絕大多數時間都是這種情況 – 然后它可以繼續(xù)直接調用非虛擬化的 Enumerable RangeIterator.MoveNext方法。不僅如此,它還認為內聯(lián) MoveNext 方法是有利可圖的。最終效果是生成的匯編代碼有點大,但針對預期最常見的確切場景進行了優(yōu)化。當我們開始構建動態(tài) PGO 時,這些就是我們想要的那種勝利。
動態(tài)PGO 將在RyuJIT 部分再次討論。
文件 IO 改進
FileStream幾乎完全用.NET 6 重寫,重點是提高異步文件IO 性能。在Windows 上,實現(xiàn)不再使用阻塞API,并且可以 快幾倍 !我們還改進了所有平臺上的內存使用。在第一次異步操作(通常分配)之后,我們已經使異步操作 免分配 !此外,我們已經使Windows 和Unix 實現(xiàn)不同的邊緣情況的行為統(tǒng)一(這是可能的)。
這種重寫的性能改進使所有操作系統(tǒng)受益。對Windows 的好處是最大的,因為它遠遠落后。macOS 和Linux 用戶也應該會看到顯著FileStream的性能改進。
以下基準將100 MB 寫入新文件。
private byte[] _bytes = new byte[8_000];[Benchmark]public async Task Write100MBAsync(){ using FileStream fs = new("File.txt", FileMode.Create, FileAccess.Write, FileShare.None, 1, FileOptions.Asynchronous); for (int i = 0; i < 100_000_000 / 8_000; i ) await fs.WriteAsync(_bytes);}
在帶有SSD 驅動器的Windows 上,我們觀察到 4倍的加速 和超過 1200倍的分配下降 :
我們還認識到需要更高性能的文件 IO 功能:并發(fā)讀取和寫入,以及分散/收集 IO。 針對這些情況,我們?yōu)?System.IO.File和 System.IO.RandomAccess類引入了新的 API。
async Task AllOrNothingAsync(string path, IreadonlyList<ReadOnlyMemory<byte>> buffers){ using SafeFileHandle handle = File.OpenHandle( path, FileMode.Create, FileAccess.Write, FileShare.None, FileOptions.Asynchronous, preallocationSize: buffers.Sum(buffer => buffer.Length)); // hint for the OS to pre-allocate disk space await RandomAccess.WriteAsync(handle, buffers, fileOffset: 0); // on Linux it's translated to a single sys-call!}
該示例演示:
- 使用新File.OpenHandle API打開文件句柄。
- 使用新的預分配大小功能預分配磁盤空間。
- 使用新的Scatter/Gather IO API寫入文件。
預分配大小功能提高了性能,因為寫入操作不需要擴展文件,并且文件不太可能被碎片化。這種方法提高了可靠性,因為寫入操作將不再因空間不足而失敗,因為空間已被保留。Scatter/Gather IO API 減少了寫入數據所需的系統(tǒng)調用次數。
更快的接口檢查和轉換
界面鑄造性能提高了16% – 38%。這種改進對于C# 與接口之間的模式匹配特別有用。
這張圖表展示了一個有代表性的基準測試的改進規(guī)模。
將.NET 運行時的一部分從C 遷移到托管C# 的最大優(yōu)勢之一是它降低了貢獻的障礙。這包括接口轉換,它作為早期的.NET 6 更改移至C#。.NET 生態(tài)系統(tǒng)中懂C# 的人比懂C 的人多(而且運行時使用具有挑戰(zhàn)性的C 模式)。僅僅能夠閱讀構成運行時的一些代碼是培養(yǎng)以各種形式做出貢獻的信心的重要一步。
歸功于 Ben Adams。
System.Text.Json 源生成器
我們?yōu)镾ystem.Text.Json 添加了一個源代碼生成器,它避免了在運行時進行反射和代碼生成的需要,并且可以在構建時生成最佳序列化代碼。序列化程序通常使用非常保守的技術編寫,因為它們必須如此。但是,如果您閱讀自己的序列化源代碼(使用序列化程序),您可以看到明顯的選擇應該是什么,可以使序列化程序在您的特定情況下更加優(yōu)化。這正是這個新的源生成器所做的。
除了提高性能和減少內存之外,源代碼生成器還生成最適合裝配修整的代碼。這有助于制作更小的應用程序。
序列化POCO是一種非常常見的場景。使用新的源代碼生成器,我們觀察到序列化速度比我們的基準 快1.6倍。
TechEmpower緩存基準測試平臺或框架對來自數據庫的信息進行內存緩存?;鶞蕼y試的.NET 實現(xiàn)執(zhí)行緩存數據的JSON 序列化,以便將其作為響應發(fā)送到測試工具。
我們觀察到約100K RPS 增益( 增加約40%)。與 MemoryCache 性能改進相結合時,.NET 6 的吞吐量比.NET 5 高50% !
C# 10
歡迎來到C# 10。C# 10 的一個主要主題是繼續(xù)從C# 9 中的頂級語句開始的簡化之旅。新功能從 Program.cs中刪除了更多的儀式,導致程序只有一行。 他們的靈感來自于與沒有 C# 經驗的人(學生、專業(yè)開發(fā)人員和其他人)交談,并了解什么對他們來說最有效且最直觀。
大多數.NET SDK 模板都已更新,以提供現(xiàn)在可以使用C# 10 實現(xiàn)的更簡單、更簡潔的體驗。我們收到反饋說,有些人不喜歡新模板,因為它們不適合專家,刪除面向對象,刪除在編寫C# 的第一天學習的重要概念,或鼓勵在一個文件中編寫整個程序。客觀地說,這些觀點都不正確。新模型同樣適用于作為專業(yè)開發(fā)人員的學生。但是,它與.NET 6 之前的C 派生模型不同。
C# 10 中還有其他一些功能和改進,包括記錄結構。
全局使用指令
全局using 指令讓您using只需指定一次指令并將其應用于您編譯的每個文件。
以下示例顯示了語法的廣度:
- global using System;
- global using static System.Console;
- global using Env = System.Environment;
您可以將global using語句放在任何 .cs 文件中,包括在 Program.cs中。
隱式 usings 是一個MSBuild 概念,它會根據SDK自動添加一組指令。例如,控制臺應用程序隱式使用不同于ASP.NET Core。
隱式使用是可選的,并在a 中啟用PropertyGroup:
- <ImplicitUsings>enable<mplicitUsings>
隱式使用對于現(xiàn)有項目是可選的,但默認包含在新C# 項目中。有關詳細信息,請參閱隱式使用。
文件范圍的命名空間
文件范圍的命名空間使您能夠聲明整個文件的命名空間,而無需將剩余內容嵌套在{ …}中. 只允許一個,并且必須在聲明任何類型之前出現(xiàn)。
新語法是單個的一行:
namespace MyNamespace;class MyClass { ... } // Not indented
這種新語法是三行縮進樣式的替代方案:
namespace MyNamespace{ class MyClass { ... } // Everything is indented}
好處是在整個文件位于同一個命名空間中的極其常見的情況下減少縮進。
記錄結構
C# 9 將記錄作為一種特殊的面向值的類形式引入。在C# 10 中,您還可以聲明結構記錄。C# 中的結構已經具有值相等,但記錄結構添加了==運算符和IEquatable<T>的實現(xiàn),以及基于值的ToString實現(xiàn):
public record struct Person{ public string FirstName { get; init; } public string LastName { get; init; }}
就像記錄類一樣,記錄結構可以是"位置的",這意味著它們有一個主構造函數,它隱式聲明與參數對應的公共成員:
public record structPerson(stringFirstName,stringLastName);
但是,與記錄類不同,隱式公共成員是_可變的自動實現(xiàn)的屬性_。這樣一來,記錄結構就成為了元組的自然成長故事。例如,如果您有一個返回類型(string FirstName, string LastName),并且您希望將其擴展為命名類型,您可以輕松地聲明相應的位置結構記錄并維護可變語義。
如果你想要一個具有只讀屬性的不可變記錄,你可以聲明整個記錄結構readonly(就像你可以其他結構一樣):
publicreadonly record structPerson(stringFirstName,stringLastName);
C# 10 不僅支持記錄結構,還支持_所有_結構以及匿名類型的with表達式:
var updatedPerson = person with{FirstName="Mary"};
F# 6
F# 6旨在讓F# 更簡單、更高效。這適用于語言設計、庫和工具。我們對F# 6(及更高版本)的目標是消除語言中讓用戶感到驚訝或阻礙學習F# 的極端情況。我們很高興能與F# 社區(qū)合作進行這項持續(xù)的努力。
讓 F# 更快、更互操作
新語法task {…}直接創(chuàng)建一個任務并啟動它。這是 F# 6 中最重要的功能之一,它使異步任務更簡單、性能更高,并且與 C# 和其他 .NET 語言的互操作性更強。以前,創(chuàng)建 .NET 任務需要使用async {…}來創(chuàng)建任務并調用Async.StartImmediateAsTask。
該功能task {…}建立在稱為“可恢復代碼”RFC FS-1087的基礎之上??苫謴痛a是一個核心特性,我們希望在未來使用它來構建其他高性能異步和屈服狀態(tài)機。
F# 6 還為庫作者添加了其他性能特性,包括InlineIfLambda 和F#活動模式的未裝箱表示。一個特別顯著的性能改進在于列表和數組表達式的編譯,現(xiàn)在它們的速度提高了 4倍 ,并且調試也更好、更簡單。
讓 F# 更易學、更統(tǒng)一
F# 6 啟用expr[idx]索引語法。到目前為止,F(xiàn)# 一直使用 expr.[idx] 進行索引。刪除點符號是基于第一次使用 F# 用戶的反復反饋,點的使用與他們期望的標準實踐有不必要的差異。在新代碼中,我們建議系統(tǒng)地使用新的expr[idx]索引語法。作為一個社區(qū),我們都應該切換到這種語法。
F# 社區(qū)為使 F# 語言在 F# 6 中更加統(tǒng)一做出了重要改進。其中最重要的是消除了 F# 縮進規(guī)則中的一些不一致和限制。使 F# 更加統(tǒng)一的其他設計添加包括添加as圖案;在計算表達式中允許“重載自定義操作”(對 DSL 有用);允許_丟棄use綁定并允許%B在輸出中進行二進制格式化。F# 核心庫添加了用于復制和更新列表、數組和序列的新函數,以及其他NativePtr內在函數。自 2.0 起棄用的 F# 的一些舊功能現(xiàn)在會導致錯誤。其中許多更改更好地使 F# 與您的期望保持一致,從而減少意外。
F# 6 還增加了對 F# 中其他“隱式”和“類型導向”轉換的支持。這意味著更少的顯式向上轉換,并為 .NET 樣式的隱式轉換添加了一流的支持。F# 也進行了調整,以更好地適應使用 64 位整數的數字庫時代,并隱式擴展了 32 位整數。
改進 F# 工具
F# 6 中的工具改進使日常編碼更容易。新的"管道調試"允許您單步執(zhí)行、設置斷點并檢查 F# 管道語法input |> f1 |> f2 的中間值。陰影值的調試顯示已得到改進,消除了調試時常見的混淆源。F# 工具現(xiàn)在也更高效,F(xiàn)# 編譯器并行執(zhí)行解析階段。F# IDE 工具也得到了改進。F# 腳本現(xiàn)在更加健壯,允許您通過global.json文件固定使用的 .NET SDK 版本。
熱重載
Hot Reload 是另一個性能特性,專注于開發(fā)人員的生產力。它使您能夠對正在運行的應用程序進行各種代碼編輯,從而縮短您等待應用程序重新構建、重新啟動或重新導航到您在進行代碼更改后所在位置所需的時間。
Hot Reload 可通過dotnet watch CLI 工具和 Visual Studio 2022 使用。您可以將 Hot Reload 與多種應用類型一起使用,例如 ASP.NET Core、Blazor、.NET MAUI、控制臺、Windows 窗體 (WinForms)、WPF、WinUI 3、Azure 函數等。
使用 CLI 時,只需使用 啟動您的 .NET 6 應用程序dotnet watch,進行任何受支持的編輯,然后在保存文件時(如在 Visual Studio Code 中),這些更改將立即應用。如果不支持更改,詳細信息將記錄到命令窗口。
此圖像顯示了一個使用dotnet watch. 我對.cs文件和.cshtml文件進行了編輯(如日志中所述),兩者都應用于代碼并在不到半秒的時間內非??焖俚胤从吃跒g覽器中。
使用 Visual Studio 2022 時,只需啟動您的應用程序,進行支持的更改,然后使用新的"熱重載"按鈕(如下圖所示)應用這些更改。您還可以通過同一按鈕上的下拉菜單選擇在保存時應用更改。使用 Visual Studio 2022 時,熱重載可用于多個 .NET 版本,適用于 .NET 5 、.NET Core 和 .NET Framework。例如,您將能夠對按鈕的OnClickEvent處理程序進行代碼隱藏更改。應用程序的Main方法不支持它。
注意:RuntimeInformation.FrameworkDescription中存在一個錯誤,該錯誤將在該圖像中展示,很快就會修復。
Hot Reload 還與現(xiàn)有的 Edit and Continue 功能(在斷點處停止時)以及用于實時編輯應用程序 UI 的 XAML Hot Reload 協(xié)同工作。目前支持 C# 和 Visual Basic 應用程序(不是 F#)。
安全
.NET 6 中的安全性得到了顯著改進。它始終是團隊關注的重點,包括威脅建模、加密和深度防御防御。
在 Linux 上,我們依賴OpenSSL進行所有加密操作,包括 TLS(HTTPS 必需)。在 macOS 和 Windows 上,我們依賴操作系統(tǒng)提供的功能來實現(xiàn)相同的目的。對于每個新版本的 .NET,我們經常需要添加對新版本 OpenSSL 的支持。.NET 6 增加了對OpenSSL 3的支持。
OpenSSL 3 的最大變化是改進的FIPS 140-2模塊和更簡單的許可。
.NET 6 需要 OpenSSL 1.1 或更高版本,并且會更喜歡它可以找到的最高安裝版本的 OpenSSL,直到并包括 v3。在一般情況下,當您使用的 Linux 發(fā)行版默認切換到 OpenSSL 3 時,您最有可能開始使用 OpenSSL 3。大多數發(fā)行版還沒有這樣做。例如,如果您在 Red Hat 8 或 Ubuntu 20.04 上安裝 .NET 6,您將不會(在撰寫本文時)開始使用 OpenSSL 3。
OpenSSL 3、Windows 10 21H1 和 Windows Server 2022 都支持ChaCha20Poly1305。您可以在.NET 6 中使用這種新的經過身份驗證的加密方案(假設您的環(huán)境支持它)。
感謝 Kevin Jones對 ChaCha20Poly1305 的 Linux 支持。
我們還發(fā)布了新的運行時安全緩解路線圖。重要的是,您使用的運行時不受教科書攻擊類型的影響。我們正在滿足這一需求。在 .NET 6 中,我們構建了W^X和英特爾控制流強制技術(CET)的初始實現(xiàn)。W^X 完全受支持,默認為 macOS Arm64 啟用,并且可以選擇加入其他環(huán)境。CET 是所有環(huán)境的選擇加入和預覽。我們希望在 .NET 7 中的所有環(huán)境中默認啟用這兩種技術。
Arm64
這些天來,對于筆記本電腦、云硬件和其他設備來說,Arm64 令人興奮不已。我們對 .NET 團隊感到同樣興奮,并正在盡最大努力跟上這一行業(yè)趨勢。我們直接與 Arm Holdings、Apple 和 Microsoft 的工程師合作,以確保我們的實施是正確和優(yōu)化的,并且我們的計劃保持一致。這些密切的合作伙伴關系對我們幫助很大。
- 特別感謝 Apple 在 M1 芯片發(fā)布之前向我們的團隊發(fā)送了一蒲式耳 Arm64 開發(fā)套件供我們使用,并提供了重要的技術支持。
- 特別感謝 Arm Holdings,他們的工程師對我們的 Arm64 更改進行了代碼審查,并進行了性能改進。
在此之前,我們通過 .NET Core 3.0 和 Arm32 添加了對 Arm64 的初始支持。該團隊在最近的幾個版本中都對 Arm64 進行了重大投資,并且在可預見的未來這將繼續(xù)下去。在 .NET 6 中,我們主要關注在 macOS 和 Windows Arm64 操作系統(tǒng)上支持新的 Apple Silicon 芯片和x64 仿真場景。
您可以在 macOS 11 和 Windows 11 Arm64 操作系統(tǒng)上安裝 Arm64 和 x64 版本的 .NET。我們必須做出多種設計選擇和產品更改以確保其奏效。
我們的策略是“親原生架構”。我們建議您始終使用與原生架構相匹配的 SDK,即 macOS 和 Windows Arm64 上的 Arm64 SDK。SDK 是大量的軟件。在 Arm64 芯片上本地運行的性能將比仿真高得多。我們更新了 CLI 以簡化操作。我們永遠不會專注于優(yōu)化模擬 x64。
默認情況下,如果您dotnet run是帶有 Arm64 SDK 的 .NET 6 應用程序,它將作為 Arm64 運行。您可以使用參數輕松切換到以 x64 運行,例如-adotnet run -a x64. 相同的論點適用于其他 CLI 動詞。有關更多信息,請參閱 適用于macOS 和Windows Arm64 的.NET 6 RC2 更新。
我想確保涵蓋其中的一個微妙之處。當您使用-a x64時,SDK 仍以 Arm64 方式原生運行。.NET SDK 體系結構中存在進程邊界的固定點。在大多數情況下,一個進程必須全是 Arm64 或全是 x64。我正在簡化一點,但 .NET CLI 會等待 SDK 架構中的最后一個進程創(chuàng)建,然后將其作為您請求的芯片架構(如 x64)啟動。這就是您的代碼運行的過程。這樣,作為開發(fā)人員,您可以獲得 Arm64 的好處,但您的代碼可以在它需要的過程中運行。這僅在您需要將某些代碼作為 x64 運行時才相關。如果你不這樣做,那么你可以一直以 Arm64 的方式運行所有東西,這很棒。
Arm64支持
對于 macOS 和 Windows Arm64,以下是您需要了解的要點:
- 支持并推薦 .NET 6 Arm64 和 x64 SDK。
- 支持所有支持的 Arm64 和 x64 運行時。
- .NET Core 3.1 和 .NET 5 SDK 可以工作,但提供的功能較少,并且在某些情況下不受完全支持。
- dotnet test尚未與 x64 仿真一起正常工作。我們正在努力。dotnet test將作為6.0.200 版本的一部分進行改進,并且可能更早。
有關更多完整信息,請參閱.NET 對macOS 和Windows Arm64的支持。
此討論中缺少Linux。它不像macOS 和Windows 那樣支持x64 仿真。因此,這些新的CLI 特性和支持方法并不直接適用于Linux,Linux 也不需要它們。
視窗Arm64
我們有一個簡單的工具來演示.NET 運行的環(huán)境。
C:Usersrich>dotnet tool install -g dotnet-runtimeinfoYou can invoke the tool using the following command: dotnet-runtimeinfoTool 'dotnet-runtimeinfo' (version '1.0.5') was successfully installed.C:Usersrich>dotnet runtimeinfo 42 42 ,d ,d 42 42 42 ,adPPYb,42 ,adPPYba, MM42MMM 8b,dPPYba, ,adPPYba, MM42MMMa8" `Y42 a8" "8a 42 42P' `"8a a8P_____42 428b 42 8b d8 42 42 42 8PP""""""" 42"8a, ,d42 "8a, ,a8" 42, 42 42 "8b, ,aa 42, `"8bbdP"Y8 `"YbbdP"' "Y428 42 42 `"Ybbd8"' "Y428**.NET informationVersion: 6.0.0FrameworkDescription: .NET 6.0.0-rtm.21522.10Libraries version: 6.0.0-rtm.21522.10Libraries hash: 4822e3c3aa77eb82b2fb33c9321f923cf11ddde6**Environment informationProcessorCount: 8OSArchitecture: Arm64OSDescription: Microsoft Windows 10.0.22494OSVersion: Microsoft Windows NT 10.0.22494.0
如您所見,該工具在Windows Arm64 上本機運行。我將向您展示http://ASP.NET Core 的樣子。
macOS Arm64
您可以看到在macOS Arm64 上的體驗是相似的,并且還展示了架構目標。
rich@MacBook-Air app % dotnet --version6.0.100rich@MacBook-Air app % dotnet --info | grep RID RID: osx-arm64rich@MacBook-Air app % cat Program.cs using System.Runtime.InteropServices;using static System.Console;WriteLine($"Hello, {RuntimeInformation.OSArchitecture} from {RuntimeInformation.FrameworkDescription}!");rich@MacBook-Air app % dotnet runHello, Arm64 from .NET 6.0.0-rtm.21522.10!rich@MacBook-Air app % dotnet run -a x64Hello, X64 from .NET 6.0.0-rtm.21522.10!rich@MacBook-Air app %
這張圖片展示了Arm64 執(zhí)行是Arm64 SDK 的默認設置,以及使用-a參數在目標Arm64 和x64 之間切換是多么容易。完全相同的體驗適用于Windows Arm64。
此圖像演示了相同的內容,但使用的是http://ASP.NET Core。我正在使用與您在上圖中看到的相同的.NET 6 Arm64 SDK。
Arm64 上的 Docker
Docker 支持在本機架構和仿真中運行的容器,本機架構是默認的。這看起來很明顯,但當大多數Docker Hub 目錄都是面向x64 時,這可能會讓人感到困惑。您可以使用-platform linux/amd64來請求x64 圖像。
我們僅支持在Arm64 操作系統(tǒng)上運行Linux Arm64 .NET 容器映像。這是因為我們從不支持在QEMU中運行.NET ,這是Docker 用于架構模擬的??磥磉@可能是由于 QEMU 的限制。
此圖像演示了我們維護的控制臺示例:mcr.microsoft.com/dotnet/samples。 這是一個有趣的示例,因為它包含一些基本邏輯,用于打印您可以使用的CPU 和內存限制信息。我展示的圖像設置了CPU 和內存限制。
自己試試吧:docker run –rm mcr.microsoft.com/dotnet/samples
Arm64 性能
Apple Silicon 和x64 仿真支持項目非常重要,但是,我們也普遍提高了Arm64 性能。
此圖像演示了將堆棧幀的內容清零的改進,這是一種常見的操作。綠線是新行為,而橙色線是另一個(不太有益的)實驗,兩者都相對于基線有所改善,由藍線表示。對于此測試,越低越好。
容器
.NET 6 更適合容器,主要基于本文中討論的所有改進,適用于Arm64 和x64。我們還進行了有助于各種場景的關鍵更改。使用.NET 6 驗證容器改進演示了其中一些改進正在一起測試。
Windows 容器改進和新環(huán)境變量也包含在11 月9 日(明天)發(fā)布的11 月.NET Framework 4.8 容器更新中。
發(fā)布說明可在我們的docker 存儲庫中找到:
- .NET 6 容器發(fā)行說明
- .NET Framework 4.8 2021 年11 月容器發(fā)行說明
Windows 容器
.NET 6 增加了對Windows 進程隔離容器的支持。如果您在 Azure Kubernetes 服務(AKS) 中使用Windows 容器,那么您依賴于進程隔離的容器。進程隔離容器可以被認為與Linux 容器非常相似。Linux 容器使用cgroups,Windows 進程隔離容器使用Job Objects。Windows 還提供Hyper-V 容器,通過更強大的虛擬化提供更大的隔離。Hyper-V 容器的.NET 6 沒有任何變化。
此更改的主要價值是現(xiàn)在Environment.ProcessorCount將使用Windows 進程隔離容器報告正確的值。如果在64 核機器上創(chuàng)建2 核容器,Environment.ProcessorCount將返回2. 在以前的版本中,此屬性將報告機器上的處理器總數,與Docker CLI、Kubernetes 或其他容器編排器/運行時指定的限制無關。此值被.NET 的各個部分用于擴展目的,包括.NET 垃圾收集器(盡管它依賴于相關的較低級別的API)。社區(qū)庫也依賴此API 進行擴展。
我們最近在AKS 上使用大量pod 在生產中的Windows 容器上與客戶驗證了這一新功能。他們能夠以50% 的內存(與他們的典型配置相比)成功運行,這是以前導致異常的OutOfMemoryException水平StackOverflowException。他們沒有花時間找到最低內存配置,但我們猜測它明顯低于他們典型內存配置的50%。由于這一變化,他們將轉向更便宜的Azure 配置,從而節(jié)省資金。只需升級即可,這是一個不錯的、輕松的勝利。
優(yōu)化縮放
我們從用戶那里聽說,某些應用程序在Environment.ProcessorCount報告正確的值時無法實現(xiàn)最佳擴展。如果這聽起來與您剛剛閱讀的有關Windows 容器的內容相反,那么它有點像。.NET 6 現(xiàn)在提供DOTNET_PROCESSOR_COUNT 環(huán)境變量來手動控制Environment.ProcessorCount的值。在典型的用例中,應用程序可能在64 核機器上配置為4核,并且在8或16核方面擴展得最好。此環(huán)境變量可用于啟用該縮放。
這個模型可能看起來很奇怪,其中Environment.ProcessorCount和–cpus(通過Docker CLI)值可能不同。默認情況下,容器運行時面向核心等價物,而不是實際核心。這意味著,當你說你想要4 個核心時,你得到的CPU 時間與4 個核心相當,但你的應用程序可能(理論上)在更多的核心上運行,甚至在短時間內在64 核機器上運行所有64 個核心。這可能使您的應用程序能夠在超過4 個線程上更好地擴展(繼續(xù)示例),并且分配更多可能是有益的。這假定線程分配基于 Environment.ProcessorCount的值。如果您選擇設置更高的值,您的應用程序可能會使用更多內存。對于某些工作負載,這是一個簡單的權衡。至少,這是一個您可以測試的新選項。
Linux 和Windows 容器均支持此新功能。
Docker 還提供了一個CPU 組功能,您的應用程序可以關聯(lián)到特定的內核。在這種情況下不建議使用此功能,因為應用程序可以訪問的內核數量是具體定義的。我們還看到了將它與Hyper-V 容器一起使用時的一些問題,并且它并不是真正適用于那種隔離模式。
Debian 11 "bullseye"
我們密切關注Linux 發(fā)行版的生命周期和發(fā)布計劃,并嘗試代表您做出最佳選擇。Debian 是我們用于默認Linux 映像的Linux 發(fā)行版。如果您6.0從我們的一個容器存儲庫中提取標簽,您將提取一個Debian 映像(假設您使用的是Linux 容器)。對于每個新的.NET 版本,我們都會考慮是否應該采用新的Debian 版本。
作為一項政策,我們不會為了方便標簽而更改Debian 版本,例如6.0, mid-release。如果我們這樣做了,某些應用程序肯定會崩潰。這意味著,在發(fā)布開始時選擇Debian 版本非常重要。此外,這些圖像得到了很多使用,主要是因為它們是"好標簽"的引用。
Debian 和.NET 版本自然不會一起計劃。當我們開始.NET 6 時,我們看到Debian "bullseye" 可能會在2021 年發(fā)布。我們決定從發(fā)布開始就押注于Bullseye。我們開始使用.NET 6 Preview 1發(fā)布基于靶心的容器映像,并決定不再回頭。賭注是.NET 6 版本會輸掉與靶心版本的競爭。到8 月8 日,我們仍然不知道Bullseye 什么時候發(fā)貨,距離我們自己的版本發(fā)布還有三個月,即11 月8 日。我們不想在預覽版Linux 上發(fā)布生產.NET 6,但我們堅持我們會輸掉這場競賽的計劃很晚。
當Debian 11 "bullseye"于8 月14 日發(fā)布時,我們感到非常驚喜。我們輸掉了比賽,但贏得了賭注。這意味著默認情況下,.NET 6 用戶從第一天開始就可以獲得最佳和最新的Debian。我們相信Debian 11 和.NET 6 將是許多用戶的絕佳組合。抱歉,克星,我們中了靶心。
較新的發(fā)行版在其軟件包提要中包含各種軟件包的較新主要版本,并且通??梢愿斓孬@得CVE 修復。這是對較新內核的補充。新發(fā)行版可以更好地為用戶服務。
再往前看,我們很快就會開始計劃對Ubuntu 22.04的支持。Ubuntu是另一個Debian 系列發(fā)行版,深受.NET 開發(fā)人員的歡迎。我們希望為新的Ubuntu LTS 版本提供當日支持。
向Tianon Gravi 致敬,感謝他們?yōu)樯鐓^(qū)維護Debian 映像并在我們有問題時幫助我們。
Dotnet Monitor
dotnet monitor是容器的重要診斷工具。它作為 sidecar 容器鏡像已經有一段時間了,但處于不受支持的"實驗"狀態(tài)。作為.NET 6 的一部分,我們正在發(fā)布一個基于.NET 6 的dotnet monitor映像,該映像在生產中得到完全支持。
dotnet monitor已被Azure App Service 用作其http://ASP.NET Core Linux 診斷體驗的實現(xiàn)細節(jié)。這是預期的場景之一,建立在dotnet monitor 之上,以提供更高級別和更高價值的體驗。
您現(xiàn)在可以拉取新圖像:
docker pull mcr.microsoft.com/dotnet/monitor:6.0
dotnet monitor使從.NET 進程訪問診斷信息(日志、跟蹤、進程轉儲)變得更加容易。在臺式機上訪問所需的所有診斷信息很容易,但是,這些熟悉的技術在使用容器的生產環(huán)境中可能不起作用。dotnet monitor提供了一種統(tǒng)一的方式來收集這些診斷工件,無論是在您的桌面計算機上還是在Kubernetes 集群中運行。收集這些診斷工件有兩種不同的機制:
- 用于臨時收集工件的 HTTP API。當您已經知道您的應用程序遇到問題并且您有興趣收集更多信息時,您可以調用這些API 端點。
- 基于規(guī)則的配置 觸發(fā)器,用于始終在線收集工件。您可以配置規(guī)則以在滿足所需條件時收集診斷數據,例如,當您持續(xù)高CPU 時收集進程轉儲。
dotnet monitor為.NET 應用程序提供了一個通用的診斷API,可以使用任何工具在任何地方工作?!巴ㄓ肁PI”不是.NET API,而是您可以調用和查詢的Web API。dotnet monitor包括一個ASP.NET Web 服務器,它直接與.NET 運行時中的診斷服務器交互并公開來自診斷服務器的數據設計dotnet monitor可實現(xiàn)生產中的高性能監(jiān)控和安全使用,以控制對特權信息的訪問。dotnet monitor通過非Internet 可尋址的unix domain socket與運行時交互——跨越容器邊界。該模型通信模型非常適合此用例。
結構化 JSON 日志
JSON 格式化程序現(xiàn)在是aspnet.NET 6 容器映像中的默認控制臺記錄器。.NET 5 中的默認設置為簡單的控制臺格式化程序。進行此更改是為了使默認配置與依賴機器可讀格式(如JSON)的自動化工具一起使用。
圖像的輸出現(xiàn)在如下所示aspnet:
$ docker run --rm -it -p 8000:80 mcr.microsoft.com/dotnet/samples:aspnetapp{"EventId":60,"LogLevel":"Warning","Category":"Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository","Message":"Storing keys in a directory u0027/root/.aspnet/DataProtection-Keysu0027 that may not be persisted outside of the container. Protected data will be unavailable when container is destroyed.","State":{"Message":"Storing keys in a directory u0027/root/.aspnet/DataProtection-Keysu0027 that may not be persisted outside of the container. Protected data will be unavailable when container is destroyed.","path":"/root/.aspnet/DataProtection-Keys","{OriginalFormat}":"Storing keys in a directory u0027{path}u0027 that may not be persisted outside of the container. Protected data will be unavailable when container is destroyed."}}{"EventId":35,"LogLevel":"Warning","Category":"Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager","Message":"No XML encryptor configured. Key {86cafacf-ab57-434a-b09c-66a929ae4fd7} may be persisted to storage in unencrypted form.","State":{"Message":"No XML encryptor configured. Key {86cafacf-ab57-434a-b09c-66a929ae4fd7} may be persisted to storage in unencrypted form.","KeyId":"86cafacf-ab57-434a-b09c-66a929ae4fd7","{OriginalFormat}":"No XML encryptor configured. Key {KeyId:B} may be persisted to storage in unencrypted form."}}{"EventId":14,"LogLevel":"Information","Category":"Microsoft.Hosting.Lifetime","Message":"Now listening on: http://[::]:80","State":{"Message":"Now listening on: http://[::]:80","address":"http://[::]:80","{OriginalFormat}":"Now listening on: {address}"}}{"EventId":0,"LogLevel":"Information","Category":"Microsoft.Hosting.Lifetime","Message":"Application started. Press Ctrlu002BC to shut down.","State":{"Message":"Application started. Press Ctrlu002BC to shut down.","{OriginalFormat}":"Application started. Press Ctrlu002BC to shut down."}}{"EventId":0,"LogLevel":"Information","Category":"Microsoft.Hosting.Lifetime","Message":"Hosting environment: Production","State":{"Message":"Hosting environment: Production","envName":"Production","{OriginalFormat}":"Hosting environment: {envName}"}}{"EventId":0,"LogLevel":"Information","Category":"Microsoft.Hosting.Lifetime","Message":"Content root path: /app","State":{"Message":"Content root path: /app","contentRoot":"/app","{OriginalFormat}":"Content root path: {contentRoot}"}}
Logging__Console__FormatterName可以通過設置或取消設置環(huán)境變量或通過代碼更改來更改記錄器格式類型(有關更多詳細信息,請參閱控制臺日志格式)。
更改后,您將看到如下輸出(就像.NET 5 一樣):
$ docker run --rm -it -p 8000:80 -e Logging__Console__FormatterName="" mcr.microsoft.com/dotnet/samples:aspnetappwarn: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[60] Storing keys in a directory '/root/.aspnet/DataProtection-Keys' that may not be persisted outside of the container. Protected data will be unavailable when container is destroyed.warn: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[35] No XML encryptor configured. Key {8d4ddd1d-ccfc-4898-9fe1-3e7403bf23a0} may be persisted to storage in unencrypted form.info: Microsoft.Hosting.Lifetime[14] Now listening on: http://[::]:80info: Microsoft.Hosting.Lifetime[0] Application started. Press Ctrl C to shut down.info: Microsoft.Hosting.Lifetime[0] Hosting environment: Productioninfo: Microsoft.Hosting.Lifetime[0] Content root path: /app
注意:此更改不會影響開發(fā)人員計算機上的.NET SDK,例如dotnet run.此更改特定于aspnet容器映像。
支持 OpenTelemetry 指標
作為我們關注可觀察性的一部分,我們一直在為最后幾個.NET 版本添加對 OpenTelemetry 的支持。在.NET 6 中,我們添加了對OpenTelemetry Metrics API的支持。通過添加對OpenTelemetry 的支持,您的應用程序可以與其他OpenTelemetry系統(tǒng)無縫互操作。
System.Diagnostics.Metrics是OpenTelemetry Metrics API 規(guī)范的.NET 實現(xiàn)。Metrics API 是專門為處理原始測量而設計的,目的是高效、同時地生成這些測量的連續(xù)摘要。
API 包括Meter可用于創(chuàng)建儀器對象的類。API 公開了四個工具類:Counter、Histogram、ObservableCounter和,ObservableGauge以支持不同的度量方案。此外,API 公開MeterListener該類以允許收聽儀器記錄的測量值,以用于聚合和分組目的。
OpenTelemetry .NET 實現(xiàn)將被擴展以使用這些新的API,這些API 添加了對Metrics 可觀察性場景的支持。
圖書館測量記錄示例
Meter meter = new Meter("io.opentelemetry.contrib.mongodb", "v1.0"); Counter<int> counter = meter.CreateCounter<int>("Requests"); counter.Add(1); counter.Add(1, KeyValuePair.Create<string, object>("request", "read"));
聽力示例
MeterListener listener = new MeterListener(); listener.InstrumentPublished = (instrument, meterListener) => { if (instrument.Name == "Requests" && instrument.Meter.Name == "io.opentelemetry.contrib.mongodb") { meterListener.EnableMeasurementEvents(instrument, null); } }; listener.SetMeasurementEventCallback<int>((instrument, measurement, tags, state) => { Console.WriteLine($"Instrument: {instrument.Name} has recorded the measurement {measurement}"); }); listener.Start();
Windows Forms
我們繼續(xù)在 Windows 窗體中進行重要改進。.NET 6 包括更好的控件可訪問性、設置應用程序范圍的默認字體、模板更新等的能力。
可訪問性改進
在此版本中,我們添加了用于CheckedListBox、LinkLabel、Panel、ScrollBar和TabControlTrackBar的UIA 提供程序,它們使講述人等工具和測試自動化能夠與應用程序的元素進行交互。
默認字體
您現(xiàn)在可以使用.Application.SetDefaultFont
voidApplication.SetDefaultFont(Font font)
最小的應用程序
以下是帶有 .NET 6 的最小Windows 窗體應用程序:
class Program{ [STAThread] static void Main() { ApplicationConfiguration.Initialize(); Application.Run(new Form1()); }}
作為.NET 6 版本的一部分,我們一直在更新大多數模板,使其更加現(xiàn)代和簡約,包括Windows 窗體。我們決定讓Windows 窗體模板更傳統(tǒng)一些,部分原因是需要將[STAThread]屬性應用于應用程序入口點。然而,還有更多的戲劇而不是立即出現(xiàn)在眼前。
ApplicationConfiguration.Initialize()是一個源生成API,它在后臺發(fā)出以下調用:
Application.EnableVisualStyles();Application.SetCompatibleTextRenderingDefault(false);Application.SetDefaultFont(new Font(...));Application.SetHighDpiMode(HighDpiMode.SystemAware);
這些調用的參數可通過csproj 或props 文件中的MSBuild 屬性進行配置。
Visual Studio 2022 中的Windows 窗體設計器也知道這些屬性(目前它只讀取默認字體),并且可以向您顯示您的應用程序,就像它在運行時一樣:
模板更新
C# 的Windows 窗體模板已更新,以支持新的應用程序引導、global using指令、文件范圍的命名空間和可為空的引用類型。
更多運行時 designers
現(xiàn)在您可以構建通用設計器(例如,報表設計器),因為.NET 6 具有設計器和與設計器相關的基礎架構所缺少的所有部分。有關詳細信息,請參閱此博客文章。
單文件應用
在.NET 6中,已為 Windows 和macOS 啟用內存中單文件應用程序。在.NET 5 中,這種部署類型僅限于 Linux。您現(xiàn)在可以為所有受支持的操作系統(tǒng)發(fā)布作為單個文件部署和啟動的單文件二進制文件。單文件應用不再將任何核心運行時程序集提取到臨時目錄。
這種擴展功能基于稱為"超級主機"的構建塊。"apphost" 是在非單文件情況下啟動應用程序的可執(zhí)行文件,例如myapp.exe或./myapp. Apphost 包含用于查找運行時、加載它并使用該運行時啟動您的應用程序的代碼。Superhost 仍然執(zhí)行其中一些任務,但使用所有CoreCLR 本機二進制文件的靜態(tài)鏈接副本。靜態(tài)鏈接是我們用來實現(xiàn)單一文件體驗的方法。本機依賴項(如NuGet 包附帶的)是單文件嵌入的顯著例外。默認情況下,它們不包含在單個文件中。例如,WPF 本機依賴項不是超級主機的一部分,因此會在單文件應用程序之外產生其他文件。您可以使用該設置IncludeNativeLibrariesForSelfExtract嵌入和提取本機依賴項。
靜態(tài)分析
我們改進了單文件分析器以允許自定義警告。如果您的API 在單文件發(fā)布中不起作用,您現(xiàn)在可以使用[RequiresAssemblyFiles]屬性對其進行標記,如果啟用了分析器,則會出現(xiàn)警告。添加該屬性還將使方法中與單個文件相關的所有警告靜音,因此您可以使用該警告將警告向上傳播到您的公共API。
當 PublishSingleFile 設置為true 時,會自動為exe 項目啟用單文件分析器,但您也可以通過將 EnableSingleFileAnalysis 設置為true 來為任何項目啟用它。 如果您想支持將庫作為單個文件應用程序的一部分,這將很有幫助。
在.NET 5 中,我們?yōu)閱挝募行袨椴煌?/span>Assembly.Location和一些其他API添加了警告。
壓縮
單文件包現(xiàn)在支持壓縮,可以通過將屬性設置EnableCompressionInSingleFile為true. 在運行時,文件會根據需要解壓縮到內存中。壓縮可以為某些場景節(jié)省大量空間。
讓我們看一下與NuGet 包資源管理器一起使用的單個文件發(fā)布(帶壓縮和不帶壓縮)。
無壓縮: 172 MB
壓縮: 71.6 MB
壓縮會顯著增加應用程序的啟動時間,尤其是在Unix 平臺上。Unix 平臺有一個不能用于壓縮的無拷貝快速啟動路徑。您應該在啟用壓縮后測試您的應用程序,看看額外的啟動成本是否可以接受。
單文件調試
目前只能使用平臺調試器(如WinDBG)來調試單文件應用程序。我們正在考慮使用更高版本的Visual Studio 2022 添加Visual Studio 調試。
macOS 上的單文件簽名
單文件應用程序現(xiàn)在滿足macOS 上的Apple 公證和簽名要求。具體更改與我們根據離散文件布局構建單文件應用程序的方式有關。
Apple 開始對macOS Catalina 實施新的簽名和公證要求。我們一直在與Apple 密切合作,以了解需求,并尋找使.NET 等開發(fā)平臺能夠在該環(huán)境中正常工作的解決方案。我們已經進行了產品更改并記錄了用戶工作流程,以滿足Apple 在最近幾個.NET 版本中的要求。剩下的差距之一是單文件簽名,這是在macOS 上分發(fā).NET 應用程序的要求,包括在macOS 商店中。
IL 修整
該團隊一直致力于為多個版本進行IL 修整。.NET 6 代表了這一旅程向前邁出的重要一步。我們一直在努力使更激進的修剪模式安全且可預測,因此有信心將其設為默認模式。TrimMode=link以前是可選功能,現(xiàn)在是默認功能。
我們有一個三管齊下的修剪策略:
- 提高平臺的修剪能力。
- 對平臺進行注釋以提供更好的警告并使其他人也能這樣做。
- 在此基礎上,讓默認的修剪模式更具侵略性,以便讓應用程序變小。
由于使用未注釋反射的應用程序的結果不可靠,修剪之前一直處于預覽狀態(tài)。有了修剪警告,體驗現(xiàn)在應該是可預測的。沒有修剪警告的應用程序應該正確修剪并且在運行時觀察到行為沒有變化。目前,只有核心的.NET 庫已經完全注解了修剪,但我們希望看到生態(tài)系統(tǒng)注釋修剪并兼容修剪
減小應用程序大小
讓我們使用SDK 工具之一的crossgen來看看這個修剪改進。它可以通過幾個修剪警告進行修剪,crossgen 團隊能夠解決。
首先,讓我們看一下將crossgen 發(fā)布為一個獨立的應用程序而無需修剪。它是80 MB(包括.NET 運行時和所有庫)。
然后我們可以嘗試(現(xiàn)在是舊版).NET 5 默認修剪模式,copyused. 結果降至55 MB。
新的.NET 6 默認修剪模式link將獨立文件大小進一步降低到36MB。
我們希望新的link修剪模式能更好地與修剪的期望保持一致:顯著節(jié)省和可預測的結果。
默認啟用警告
修剪警告告訴您修剪可能會刪除運行時使用的代碼的地方。這些警告以前默認禁用,因為警告非常嘈雜,主要是由于 .NET 平臺沒有參與修剪作為第一類場景。
我們對大部分 .NET 庫進行了注釋,以便它們產生準確的修剪警告。因此,我們覺得是時候默認啟用修剪警告了。http://ASP.NET Core 和 Windows 桌面運行時庫尚未注釋。我們計劃接下來注釋 http://ASP.NET 服務組件(在 .NET 6 之后)。我們希望看到社區(qū)在 .NET 6 發(fā)布后對 NuGet 庫進行注釋。
您可以通過設置<SuppressTrimAnalysisWarnings>為true來禁用警告。
更多信息:
- 修剪警告
- 修剪介紹
- 準備.NET 庫以進行修剪
與本機 AOT 共享
我們也為Native AOT實驗實現(xiàn)了相同的修剪警告,這應該會以幾乎相同的方式改善 Native AOT 編譯體驗。
數學
我們顯著改進了數學 API。社區(qū)中的一些人已經在享受這些改進。
面向性能的 API
System.Math 中添加了面向性能的數學 API。如果底層硬件支持,它們的實現(xiàn)是硬件加速的。
新 API:
- SinCos用于同時計算Sin和Cos。
- ReciprocalEstimate用于計算 1 / x的近似值。
- ReciprocalSqrtEstimate用于計算1 / Sqrt(x)的近似值。
新的重載:
- Clamp, DivRem,Min和Max支持nint和nuint。
- Abs和Sign支持nint。
- DivRem 變體返回tuple。
性能改進:
- ScaleB被移植到C# 導致調用速度提高了93%。感謝亞歷克斯·科文頓。
大整數性能
改進了從十進制和十六進制字符串中解析 BigIntegers。我們看到了高達89% 的改進,如下圖所示(越低越好)。
感謝約瑟夫·達席爾瓦。
Complex API 現(xiàn)在注釋為 readonly
現(xiàn)在對各種API 進行了注釋,System.Numerics.Complexreadonly以確保不會對readonly值或傳遞的值進行復制in。
歸功于hrrrrustic 。
BitConverter 現(xiàn)在支持浮點到無符號整數位廣播
BitConverter 現(xiàn)在支持DoubleToUInt64Bits, HalfToUInt16Bits, SingleToUInt32Bits, UInt16BitsToHalf, UInt32BitsToSingle, 和UInt64BitsToDouble. 這應該使得在需要時更容易進行浮點位操作。
歸功于Michal Petryka 。
BitOperations 支持附加功能
BitOperations現(xiàn)在支持IsPow2,RoundUpToPowerOf2和提供nint/nuint重載現(xiàn)有函數。
感謝約翰凱利、霍耀源和羅賓林德納。
Vector, Vector2, Vector3 和 Vector4 改進
Vector現(xiàn)在支持C# 9 中添加的原始類型nint和nuint原始類型。例如,此更改應該可以更簡單地使用帶有指針或平臺相關長度類型的SIMD 指令。
Vector現(xiàn)在支持一種Sum方法來簡化計算向量中所有元素的“水平和”的需要。歸功于伊萬茲拉塔諾夫。
Vector現(xiàn)在支持一種通用方法As<TFrom, TTo>來簡化在具體類型未知的通用上下文中處理向量。感謝霍耀源
重載支持Span已添加到Vector2、Vector3和Vector4以改善需要加載或存儲矢量類型時的體驗。
更好地解析標準數字格式
我們改進了標準數字類型的解析器,特別是.ToString和.TryFormatParse。他們現(xiàn)在將理解對精度 >99 位小數的要求,并將為那么多位數提供準確的結果。此外,解析器現(xiàn)在更好地支持方法中的尾隨零。
以下示例演示了之前和之后的行為。
- 32.ToString("C100")->C132
- .NET 6:$32.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
- .NET 5:我們在格式化代碼中人為限制只能處理 <= 99 的精度。對于精度 >= 100,我們改為將輸入解釋為自定義格式。
- 32.ToString("H99")->扔一個FormatException
- .NET 6:拋出 FormatException
- 這是正確的行為,但在這里調用它是為了與下一個示例進行對比。
- 32.ToString("H100")->H132
- .NET 6:拋出FormatException
- .NET 5:H是無效的格式說明符。所以,我們應該拋出一個FormatException. 相反,我們將精度 >= 100 解釋為自定義格式的錯誤行為意味著我們返回了錯誤的值。
- double.Parse("9007199254740997.0")->9007199254740998
- .NET 6 9007199254740996:。
- .NET 5:9007199254740997.0不能完全以 IEEE 754 格式表示。使用我們當前的舍入方案,正確的返回值應該是9007199254740996. 但是,輸入的最后一部分迫使解析器錯誤地舍入結果并返回。.09007199254740998
System.Text.Json
System.Text.Json提供多種高性能API 用于處理JSON 文檔。在過去的幾個版本中,我們添加了新功能,以進一步提高JSON 處理性能并減輕對希望從NewtonSoft.Json遷移的人的阻礙。 此版本包括在該路徑上的繼續(xù),并且在性能方面向前邁出了一大步,特別是在序列化程序源生成器方面。
JsonSerializer 源生成
注意:使用.NET 6 RC1 或更早版本的源代碼生成的應用程序應重新編譯。
幾乎所有.NET 序列化程序的支柱都是反射。反射對于某些場景來說是一種很好的能力,但不能作為高性能云原生應用程序(通常(反)序列化和處理大量JSON 文檔)的基礎。反射是啟動、內存使用和程序集修整的問題。
運行時反射的替代方法是編譯時源代碼生成。在.NET 6 中,我們包含一個新的源代碼生成器作為 System.Text.Json. JSON 源代碼生成器可以與多種方式結合使用JsonSerializer并且可以通過多種方式進行配置。
它可以提供以下好處:
- 減少啟動時間
- 提高序列化吞吐量
- 減少私有內存使用
- 刪除運行時使用System.Reflection 和System.Reflection.Emit
- IL 修整兼容性
默認情況下,JSON 源生成器為給定的可序列化類型發(fā)出序列化邏輯。JsonSerializer通過生成直接使用的源代碼,這提供了比使用現(xiàn)有方法更高的性能Utf8JsonWriter。簡而言之,源代碼生成器提供了一種在編譯時為您提供不同實現(xiàn)的方法,以使運行時體驗更好。
給定一個簡單的類型:
namespace Test{ internal class JsonMessage { public string Message { get; set; } }}
源生成器可以配置為為示例JsonMessage類型的實例生成序列化邏輯。請注意,類名JsonContext是任意的。您可以為生成的源使用所需的任何類名。
using System.Text.Json.Serialization;namespace Test{ [JsonSerializable(typeof(JsonMessage)] internal partial class JsonContext : JsonSerializerContext { }}
使用此模式的序列化程序調用可能類似于以下示例。此示例提供了可能的最佳性能。
using MemoryStream ms = new();using Utf8JsonWriter writer = new(ms);JsonSerializer.Serialize(jsonMessage, JsonContext.Default.JsonMessage);writer.Flush();// Writer contains:// {"Message":"Hello, world!"}
最快和最優(yōu)化的源代碼生成模式——基于Utf8JsonWriter——目前僅可用于序列化。Utf8JsonReader根據您的反饋,將來可能會提供對反序列化的類似支持。
源生成器還發(fā)出類型元數據初始化邏輯,這也有利于反序列化。JsonMessage要反序列化使用預生成類型元數據的實例,您可以執(zhí)行以下操作:
JsonSerializer.Deserialize(json, JsonContext.Default.JsonMessage);
JsonSerializer 支持 IAsyncEnumerable
您現(xiàn)在可以使用System.Text.Json(反)序列化IAsyncEnumerableJSON 數組。以下示例使用流作為任何異步數據源的表示。源可以是本地計算機上的文件,也可以是數據庫查詢或Web 服務API 調用的結果。
JsonSerializer.SerializeAsync已更新以識別并為IAsyncEnumerable值提供特殊處理。
using System;using System.Collections.Generic;using System.IO;using System.Text.Json;static async IAsyncEnumerable<int> PrintNumbers(int n){ for (int i = 0; i < n; i ) yield return i;}using Stream stream = Console.OpenStandardOutput();var data = new { Data = PrintNumbers(3) };await JsonSerializer.SerializeAsync(stream, data); // prints {"Data":[0,1,2]}
IAsyncEnumerable僅使用異步序列化方法支持值。嘗試使用同步方法進行序列化將導致NotSupportedException被拋出。
流式反序列化需要一個新的 API 來返回IAsyncEnumerable<T>。我們?yōu)榇颂砑恿?/span>JsonSerializer.DeserializeAsyncEnumerable方法,您可以在以下示例中看到。
using System;using System.IO;using System.Text;using System.Text.Json;var stream = new MemoryStream(Encoding.UTF8.GetBytes("[0,1,2,3,4]"));await foreach (int item in JsonSerializer.DeserializeAsyncEnumerable<int>(stream)){ Console.WriteLine(item);}
此示例將按需反序列化元素,并且在使用特別大的數據流時非常有用。它僅支持從根級JSON 數組讀取,盡管將來可能會根據反饋放寬。
現(xiàn)有DeserializeAsync方法名義上支持IAsyncEnumerable<T>,但在其非流方法簽名的范圍內。它必須將最終結果作為單個值返回,如以下示例所示。
using System;using System.Collections.Generic;using System.IO;using System.Text;using System.Text.Json;var stream = new MemoryStream(Encoding.UTF8.GetBytes(@"{""Data"":[0,1,2,3,4]}"));var result = await JsonSerializer.DeserializeAsync<MyPoco>(stream);await foreach (int item in result.Data){ Console.WriteLine(item);}public class MyPoco{ public IAsyncEnumerable<int> Data { get; set; }}
在此示例中,反序列化器將IAsyncEnumerable在返回反序列化對象之前緩沖內存中的所有內容。這是因為反序列化器需要在返回結果之前消耗整個 JSON 值。
System.Text.Json:可寫 DOM 功能
可寫JSON DOM 特性為System.Text.Json添加了一個新的簡單且高性能的編程模型。這個新的API 很有吸引力,因為它避免了需要強類型的序列化合約,并且與現(xiàn)有的JsonDocument類型相比,DOM 是可變的。
這個新的 API 有以下好處:
- 在使用POCO類型是不可能或不希望的情況下,或者當JSON 模式不固定且必須檢查的情況下,序列化的輕量級替代方案。
- 啟用對大樹子集的有效修改。例如,可以有效地導航到大型JSON 樹的子部分并從該子部分讀取數組或反序列化POCO。LINQ 也可以與它一起使用。
以下示例演示了新的編程模型。
// Parse a JSON object JsonNode jNode = JsonNode.Parse("{"MyProperty":42}"); int value = (int)jNode["MyProperty"]; Debug.Assert(value == 42); // or value = jNode["MyProperty"].GetValue<int>(); Debug.Assert(value == 42); // Parse a JSON array jNode = JsonNode.Parse("[10,11,12]"); value = (int)jNode[1]; Debug.Assert(value == 11); // or value = jNode[1].GetValue<int>(); Debug.Assert(value == 11); // Create a new JsonObject using object initializers and array params var jObject = new JsonObject { ["MyChildObject"] = new JsonObject { ["MyProperty"] = "Hello", ["MyArray"] = new JsonArray(10, 11, 12) } }; // Obtain the JSON from the new JsonObject string json = jObject.ToJsonString(); Console.WriteLine(json); // {"MyChildObject":{"MyProperty":"Hello","MyArray":[10,11,12]}} // Indexers for property names and array elements are supported and can be chainedDebug.Assert(jObject["MyChildObject"]["MyArray"][1].GetValue<int>() == 11);
ReferenceHandler.IgnoreCycles
JsonSerializer(System.Text.Json)現(xiàn)在支持在序列化對象圖時忽略循環(huán)的能力。該ReferenceHandler.IgnoreCycles選項具有與Newtonsoft.Json ReferenceLoopHandling.Ignore類似的行為。一個關鍵區(qū)別是System.Text.Json 實現(xiàn)用null JSON 標記替換引用循環(huán),而不是忽略對象引用。
您可以在以下示例中看到ReferenceHandler.IgnoreCycles的行為。在這種情況下,該Next屬性被序列化為null,因為否則它會創(chuàng)建一個循環(huán)。
class Node{ public string Description { get; set; } public object Next { get; set; }}void Test(){ var node = new Node { Description = "Node 1" }; node.Next = node; var opts = new JsonSerializerOptions { ReferenceHandler = ReferenceHandler.IgnoreCycles }; string json = JsonSerializer.Serialize(node, opts); Console.WriteLine(json); // Prints {"Description":"Node 1","Next":null}}
源代碼構建
通過源代碼構建,您只需幾個命令即可在您自己的計算機上從源代碼構建.NET SDK 。讓我解釋一下為什么這個項目很重要。
源代碼構建是一個場景,也是我們在發(fā)布.NET Core 1.0 之前一直與Red Hat 合作開發(fā)的基礎架構。幾年后,我們非常接近于交付它的全自動版本。對于Red Hat Enterprise Linux (RHEL) .NET 用戶來說,這個功能很重要。Red Hat 告訴我們,.NET 已經發(fā)展成為其生態(tài)系統(tǒng)的重要開發(fā)者平臺。好的!
Linux 發(fā)行版的黃金標準是使用作為發(fā)行版存檔一部分的編譯器和工具鏈構建開源代碼。這適用于.NET 運行時(用C 編寫),但不適用于任何用C# 編寫的代碼。對于C# 代碼,我們使用兩遍構建機制來滿足發(fā)行版要求。這有點復雜,但了解流程很重要。
Red Hat 使用.NET SDK (#1) 的Microsoft 二進制構建來構建.NET SDK 源代碼,以生成SDK (#2) 的純開源二進制構建。之后,使用這個新版本的SDK (#2) 再次構建相同的SDK 源代碼,以生成可證明的開源SDK (#3)。.NET SDK (#3) 的最終二進制版本隨后可供RHEL 用戶使用。之后,Red Hat 可以使用相同的SDK (#3) 來構建新的.NET 版本,而不再需要使用Microsoft SDK 來構建每月更新。
這個過程可能令人驚訝和困惑。開源發(fā)行版需要通過開源工具構建。此模式確保不需要Microsoft 構建的SDK,無論是有意還是無意。作為開發(fā)者平臺,包含在發(fā)行版中的門檻比僅使用兼容許可證的門檻更高。源代碼構建項目使.NET 能夠滿足該標準。
源代碼構建的可交付成果是源代碼壓縮包。源tarball 包含SDK 的所有源(對于給定版本)。從那里,紅帽(或其他組織)可以構建自己的SDK 版本。Red Hat 政策要求使用內置源工具鏈來生成二進制tar 球,這就是他們使用兩遍方法的原因。但是源代碼構建本身不需要這種兩遍方法。
在Linux 生態(tài)系統(tǒng)中,給定組件同時擁有源和二進制包或tarball 是很常見的。我們已經有了可用的二進制tarball,現(xiàn)在也有了源tarball。這使得.NET 與標準組件模式相匹配。
.NET 6 的重大改進是源tarball 現(xiàn)在是我們構建的產品。它過去需要大量的人工來制作,這也導致將源tarball 交付給Red Hat 的延遲很長。雙方都對此不滿意。
在這個項目上,我們與紅帽密切合作五年多。它的成功在很大程度上要歸功于我們有幸與之共事的優(yōu)秀紅帽工程師的努力。其他發(fā)行版和組織已經并將從他們的努力中受益。
附帶說明一下,源代碼構建是朝著可重現(xiàn)構建邁出的一大步,我們也堅信這一點。.NET SDK 和C# 編譯器具有重要的可重現(xiàn)構建功能。
庫 API
除了已經涵蓋的API 之外,還添加了以下API。
WebSocket 壓縮
壓縮對于通過網絡傳輸的任何數據都很重要。WebSockets 現(xiàn)在啟用壓縮。我們使用了WebSockets 的擴展permessage-deflate實現(xiàn),RFC 7692。它允許使用該DEFLATE算法壓縮WebSockets 消息負載。此功能是GitHub 上Networking 的主要用戶請求之一。
與加密一起使用的壓縮可能會導致攻擊,例如CRIME和BREACH。這意味著不能在單個壓縮上下文中將秘密與用戶生成的數據一起發(fā)送,否則可以提取該秘密。為了讓用戶注意到這些影響并幫助他們權衡風險,我們將其中一個關鍵API 命名為DangerousDeflateOptions。我們還添加了關閉特定消息壓縮的功能,因此如果用戶想要發(fā)送秘密,他們可以在不壓縮的情況下安全地執(zhí)行此操作。
禁用壓縮時WebSocket的內存占用減少了約27%。
從客戶端啟用壓縮很容易,如下例所示。但是,請記住,服務器可以協(xié)商設置,例如請求更小的窗口或完全拒絕壓縮。
var cws = new ClientWebSocket();cws.Options.DangerousDeflateOptions = new WebSocketDeflateOptions(){ ClientMaxWindowBits = 10, ServerMaxWindowBits = 10};
還添加了對 ASP.NET Core 的 WebSocket 壓縮支持。
歸功于伊萬茲拉塔諾夫。
Socks 代理支持
SOCKS是一種代理服務器實現(xiàn),可以處理任何TCP 或UDP 流量,使其成為一個非常通用的系統(tǒng)。這是一個長期存在的社區(qū)請求,已添加到.NET 6中。
此更改增加了對Socks4、Socks4a 和Socks5 的支持。例如,它可以通過SSH 測試外部連接或連接到 Tor 網絡。
該類WebProxy現(xiàn)在接受socks方案,如以下示例所示。
var handler = new HttpClientHandler{ Proxy = new WebProxy("socks5://127.0.0.1", 9050)};var httpClient = new HttpClient(handler);
歸功于Huo yaoyuan。
Microsoft.Extensions.Hosting — 配置主機選項 API
我們在IHostBuilder 上添加了一個新的ConfigureHostOptions API,以簡化應用程序設置(例如,配置關閉超時):
using HostBuilder host = new() .ConfigureHostOptions(o => { o.ShutdownTimeout = TimeSpan.FromMinutes(10); }) .Build();host.Run();
在.NET 5 中,配置主機選項有點復雜:
using HostBuilder host = new() .ConfigureServices(services => { services.Configure<HostOptions>(o => { o.ShutdownTimeout = TimeSpan.FromMinutes(10); }); }) .Build();host.Run();
Microsoft.Extensions.DependencyInjection — CreateAsyncScope API
CreateAsyncScope創(chuàng)建API是為了處理服務的處置IAsyncDisposable。以前,您可能已經注意到處置IAsyncDisposable服務提供者可能會引發(fā)InvalidOperationException異常。
以下示例演示了新模式,CreateAsyncScope用于啟用using語句的安全使用。
await using (var scope = provider.CreateAsyncScope()){ var foo = scope.ServiceProvider.GetRequiredService<Foo>();}
以下示例演示了現(xiàn)有的問題案例:
using System;using System.Threading.Tasks;using Microsoft.Extensions.DependencyInjection;await using var provider = new ServiceCollection() .AddScoped<Foo>() .BuildServiceProvider();// This using can throw InvalidOperationExceptionusing (var scope = provider.CreateScope()){ var foo = scope.ServiceProvider.GetRequiredService<Foo>();}class Foo : IAsyncDisposable{ public ValueTask DisposeAsync() => default;}
以下模式是先前建議的避免異常的解決方法。不再需要它。
var scope = provider.CreateScope();var foo = scope.ServiceProvider.GetRequiredService<Foo>();await ((IAsyncDisposable)scope).DisposeAsync();
感謝Martin Bj?rkstr?m 。
Microsoft.Extensions.Logging — 編譯時源生成器
.NET 6 引入了LoggerMessageAttribute類型。 此屬性是Microsoft.Extensions.Logging命名空間的一部分,使用時,它會源生成高性能日志記錄API。源生成日志支持旨在為現(xiàn)代.NET 應用程序提供高度可用和高性能的日志解決方案。自動生成的源代碼依賴于ILogger接口和LoggerMessage.Define功能。
LoggerMessageAttribute源生成器在用于partial日志記錄方法時觸發(fā)。當被觸發(fā)時,它要么能夠自動生成partial它正在裝飾的方法的實現(xiàn),要么生成編譯時診斷,并提供有關正確使用的提示。編譯時日志記錄解決方案在運行時通常比現(xiàn)有的日志記錄方法快得多。它通過最大限度地消除裝箱、臨時分配和副本來實現(xiàn)這一點。
與直接手動使用LoggerMessage.Define API相比,有以下好處:
- 更短更簡單的語法:聲明性屬性使用而不是編碼樣板。
- 引導式開發(fā)人員體驗:生成器發(fā)出警告以幫助開發(fā)人員做正確的事情。
- 支持任意數量的日志記錄參數。LoggerMessage.Define最多支持六個。
- 支持動態(tài)日志級別。這是LoggerMessage.Define單獨不可能的。
要使用LoggerMessageAttribute,消費類和方法需要是partial。代碼生成器在編譯時觸發(fā)并生成partial方法的實現(xiàn)。
public static partial class Log{ [LoggerMessage(EventId = 0, Level = LogLevel.Critical, Message = "Could not open socket to `{hostName}`")] public static partial void CouldNotOpenSocket(ILogger logger, string hostName);}
在前面的示例中,日志記錄方法是static,并且在屬性定義中指定了日志級別。在靜態(tài)上下文中使用屬性時,ILogger需要實例作為參數。您也可以選擇在非靜態(tài)上下文中使用該屬性。有關更多示例和使用場景,請訪問編譯時日志記錄源生成器文檔。
System.Linq — 可枚舉的支持 Index 和 Range 參數
該Enumerable.ElementAt方法現(xiàn)在接受來自可枚舉末尾的索引,如以下示例所示。
Enumerable.Range(1, 10).ElementAt(^2); // returns 9
添加了一個Enumerable.Take接受Range參數的重載。它簡化了對可枚舉序列的切片:
- source.Take(..3)代替source.Take(3)
- source.Take(3..)代替source.Skip(3)
- source.Take(2..7)代替source.Take(7).Skip(2)
- source.Take(^3..)代替source.TakeLast(3)
- source.Take(..^3)代替source.SkipLast(3)
- source.Take(7..3)而不是.source.TakeLast(7).SkipLast(3)
感謝@dixin 。
System.Linq — TryGetNonEnumeratedCount
該TryGetNonEnumeratedCount方法嘗試在不強制枚舉的情況下獲取源可枚舉的計數。這種方法在枚舉之前預分配緩沖區(qū)很有用的場景中很有用,如下面的示例所示。
List<T> buffer = source.TryGetNonEnumeratedCount(out int count) ? new List<T>(capacity: count) : new List<T>();foreach (T item in source){ buffer.Add(item);}
TryGetNonEnumeratedCount檢查實現(xiàn)ICollection/ ICollection<T>;或利用Linq 采用的一些內部優(yōu)化的源。
System.Linq — DistinctBy / UnionBy / IntersectBy / ExceptBy
新變體已添加到允許使用鍵選擇器函數指定相等性的集合操作中,如下例所示。
Enumerable.Range(1, 20).DistinctBy(x => x % 3); // {1, 2, 3}var first = new (string Name, int Age)[] { ("Francis", 20), ("Lindsey", 30), ("Ashley", 40) };var second = new (string Name, int Age)[] { ("Claire", 30), ("Pat", 30), ("Drew", 33) };first.UnionBy(second, person => person.Age); // { ("Francis", 20), ("Lindsey", 30), ("Ashley", 40), ("Drew", 33) }
System.Linq – MaxBy / MinBy
MaxBy和MinBy方法允許使用鍵選擇器查找最大或最小元素,如下例所示。
var people = new (string Name, int Age)[] { ("Francis", 20), ("Lindsey", 30), ("Ashley", 40) };people.MaxBy(person => person.Age); // ("Ashley", 40)
System.Linq — Chunk
Chunk可用于將可枚舉的源分塊為固定大小的切片,如下例所示。
IEnumerable<int[]> chunks = Enumerable.Range(0, 10).Chunk(size: 3); // { {0,1,2}, {3,4,5}, {6,7,8}, {9} }
歸功于羅伯特安德森。
System.Linq—— // FirstOrDefault 采用默認參數的重載 LastOrDefaultSingleOrDefault
如果源可枚舉為空,則現(xiàn)有的FirstOrDefault /LastOrDefault /SingleOrDefault方法返回default(T)。添加了新的重載,它們接受在這種情況下返回的默認參數,如以下示例所示。
Enumerable.Empty<int>().SingleOrDefault(-1); // returns -1
感謝@ Foxtrek64 。
System.Linq — Zip 接受三個可枚舉的重載
Zip方法現(xiàn)在支持組合三個枚舉,如以下示例所示。
var xs = Enumerable.Range(1, 10);var ys = xs.Select(x => x.ToString());var zs = xs.Select(x => x % 2 == 0);foreach ((int x, string y, bool z) in Enumerable.Zip(xs,ys,zs)){}
歸功于Huo yaoyuan。
優(yōu)先隊列
PriorityQueue<TElement, TPriority>(System.Collections.Generic) 是一個新集合,可以添加具有值和優(yōu)先級的新項目。在出隊時,PriorityQueue 返回具有最低優(yōu)先級值的元素。您可以認為這個新集合類似于Queue<T>但每個入隊元素都有一個影響出隊行為的優(yōu)先級值。
以下示例演示了.PriorityQueue<string, int>
// creates a priority queue of strings with integer prioritiesvar pq = new PriorityQueue<string, int>();// enqueue elements with associated prioritiespq.Enqueue("A", 3);pq.Enqueue("B", 1);pq.Enqueue("C", 2);pq.Enqueue("D", 3);pq.Dequeue(); // returns "B"pq.Dequeue(); // returns "C"pq.Dequeue(); // either "A" or "D", stability is not guaranteed.
歸功于Patryk Golebiowski。
更快地將結構處理為字典值
CollectionsMarshal.GetValueRef是一個新的 不安全 API,它可以更快地更新字典中的結構值。新API 旨在用于高性能場景,而不是用于一般用途。它返回ref結構值,然后可以使用典型技術對其進行更新。
以下示例演示了如何使用新API:
ref MyStruct value = CollectionsMarshal.GetValueRef(dictionary, key);// Returns Unsafe.NullRef<TValue>() if it doesn't exist; check using Unsafe.IsNullRef(ref value)if (!Unsafe.IsNullRef(ref value)){ // Mutate in-place value.MyInt ;}
在此更改之前,更新struct字典值對于高性能場景可能會很昂貴,需要字典查找和復制到堆棧的struct. 然后在更改之后struct,它將再次分配給字典鍵,從而導致另一個查找和復制操作。這種改進將密鑰散列減少到1(從2)并刪除了所有結構復制操作。
歸功于本亞當斯。
新建 DateOnly 和 TimeOnly 結構
添加了僅限日期和時間的結構,具有以下特征:
- 每個都代表a 的一半DateTime,或者只是日期部分,或者只是時間部分。
- DateOnly非常適合生日、周年紀念日和工作日。它與SQL Server 的date類型一致。
- TimeOnly非常適合定期會議、鬧鐘和每周工作時間。它與SQL Server 的time類型一致。
- 補充現(xiàn)有的日期/時間類型( DateTime, DateTimeOffset, TimeSpan, TimeZoneInfo)。
- 在System命名空間中,在CoreLib 中提供,就像現(xiàn)有的相關類型一樣。
性能改進 DateTime.UtcNow
這種改進具有以下好處:
- 修復了在Windows 上獲取系統(tǒng)時間的2.5 倍性能回歸。
- 利用Windows 閏秒數據的5 分鐘滑動緩存,而不是在每次調用時獲取。
在所有平臺上支持 Windows 和 IANA 時區(qū)
這種改進具有以下好處:
- 使用時的隱式轉換(https://github.com/dotnet/runtime/pull/49412)TimeZoneInfo.FindSystemTimeZoneById
- TimeZoneInfo通過: TryConvertIanaIdToWindowsId、TryConvertWindowsIdToIanaId和HasIanaId(https://github.com/dotnet/runtime/issues/49407)上的新API 進行顯式轉換
- 改進了使用不同時區(qū)類型的系統(tǒng)之間的跨平臺支持和互操作。
- 刪除需要使用TimeZoneConverter OSS 庫。該功能現(xiàn)在是內置的。
改進的時區(qū)顯示名稱
Unix 上的時區(qū)顯示名稱已得到改進:
- 消除由.返回的列表中的顯示名稱的歧義。TimeZoneInfo.GetSystemTimeZones
- 利用ICU / CLDR 全球化數據。
- 僅適用于Unix。Windows 仍然使用注冊表數據。這可能會在以后更改。
還進行了以下附加改進:
- UTC 時區(qū)的顯示名稱和標準名稱被硬編碼為英語,現(xiàn)在使用與其余時區(qū)數據相同的語言(CurrentUICulture在Unix 上,Windows 上的操作系統(tǒng)默認語言)。
- 由于大小限制,Wasm 中的時區(qū)顯示名稱改為使用非本地化IANA ID。
- TimeZoneInfo.AdjustmentRule嵌套類將其BaseUtcOffsetDelta內部屬性公開,并獲得一個新的構造函數,該構造函數baseUtcOffsetDelta作為參數。(https://github.com/dotnet/runtime/issues/50256)
- TimeZoneInfo.AdjustmentRule還獲得了在Unix 上加載時區(qū)的各種修復(https://github.com/dotnet/runtime/pull/49733), (https://github.com/dotnet/runtime/pull/50131)
改進了對 Windows ACL 的支持
System.Threading.AccessControl現(xiàn)在包括對與Windows 訪問控制列表(ACL) 交互的改進支持。新的重載被添加到Mutex和Semaphore的OpenExisting和TryOpenExisting方法EventWaitHandle中。這些具有“安全權限”實例的重載允許打開使用特殊Windows 安全屬性創(chuàng)建的線程同步對象的現(xiàn)有實例。
此更新與.NET Framework 中可用的API 匹配并且具有相同的行為。
以下示例演示了如何使用這些新API。
對于Mutex:
var rights = MutexRights.FullControl;string mutexName = "MyMutexName";var security = new MutexSecurity();SecurityIdentifier identity = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null);MutexAccessRule accessRule = new MutexAccessRule(identity, rights, AccessControlType.Allow);security.AddAccessRule(accessRule);// createdMutex, openedMutex1 and openedMutex2 point to the same mutexMutex createdMutex = MutexAcl.Create(initiallyOwned: true, mutexName, out bool createdNew, security);Mutex openedMutex1 = MutexAcl.OpenExisting(mutexName, rights);MutexAcl.TryOpenExisting(mutexName, rights, out Mutex openedMutex2);
為了Semaphore
var rights = SemaphoreRights.FullControl;string semaphoreName = "MySemaphoreName";var security = new SemaphoreSecurity();SecurityIdentifier identity = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null);SemaphoreAccessRule accessRule = new SemaphoreAccessRule(identity, rights, AccessControlType.Allow);security.AddAccessRule(accessRule);// createdSemaphore, openedSemaphore1 and openedSemaphore2 point to the same semaphoreSemaphore createdSemaphore = SemaphoreAcl.Create(initialCount: 1, maximumCount: 3, semaphoreName, out bool createdNew, security);Semaphore openedSemaphore1 = SemaphoreAcl.OpenExisting(semaphoreName, rights);SemaphoreAcl.TryOpenExisting(semaphoreName, rights, out Semaphore openedSemaphore2);
為了EventWaitHandle
var rights = EventWaitHandleRights.FullControl;string eventWaitHandleName = "MyEventWaitHandleName";var security = new EventWaitHandleSecurity();SecurityIdentifier identity = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null);EventWaitHandleAccessRule accessRule = new EventWaitHandleAccessRule(identity, rights, AccessControlType.Allow);security.AddAccessRule(accessRule);// createdHandle, openedHandle1 and openedHandle2 point to the same event wait handleEventWaitHandle createdHandle = EventWaitHandleAcl.Create(initialState: true, EventResetMode.AutoReset, eventWaitHandleName, out bool createdNew, security);EventWaitHandle openedHandle1 = EventWaitHandleAcl.OpenExisting(eventWaitHandleName, rights);EventWaitHandleAcl.TryOpenExisting(eventWaitHandleName, rights, out EventWaitHandle openedHandle2);
HMAC 一次性方法
System.Security.CryptographyHMAC類現(xiàn)在具有允許一次性計算HMAC而無需分配的靜態(tài)方法。這些添加類似于在先前版本中添加的用于哈希生成的一次性方法。
DependentHandle 現(xiàn)已公開
該DependentHandle類型現(xiàn)在是公共的,具有以下 API 表面:
namespace System.Runtime{ public struct DependentHandle : IDisposable { public DependentHandle(object? target, object? dependent); public bool IsAllocated { get; } public object? Target { get; set; } public object? Dependent { get; set; } public (object? Target, object? Dependent) TargetAndDependent { get; } public void Dispose(); }}
它可用于創(chuàng)建高級系統(tǒng),例如復雜的緩存系統(tǒng)或ConditionalWeakTable<TKey, TValue>類型?的自定義版本。例如,它將被MVVM Toolkit中的WeakReferenceMessenger類型使用,以避免在廣播消息時分配內存。
可移植線程池
.NET 線程池已作為托管實現(xiàn)重新實現(xiàn),現(xiàn)在用作.NET 6 中的默認線程池。我們進行此更改以使所有.NET 應用程序都可以訪問同一個線程池,而不管是否正在使用CoreCLR、Mono 或任何其他運行時。作為此更改的一部分,我們沒有觀察到或預期任何功能或性能影響。
RyuJIT
該團隊在此版本中對.NET JIT 編譯器進行了許多改進,在每個預覽帖子中都有記錄。這些更改中的大多數都提高了性能。這里介紹了一些RyuJIT 的亮點。
動態(tài) PGO
在.NET 6 中,我們啟用了兩種形式的PGO(配置文件引導優(yōu)化):
- 動態(tài) PGO使用從當前運行中收集的數據來優(yōu)化當前運行。
- 靜態(tài)PGO依靠從過去運行中收集的數據來優(yōu)化未來運行。
動態(tài)PGO 已經在文章前面的性能部分中介紹過。我將提供一個重新上限。
動態(tài)PGO 使JIT 能夠在運行時收集有關實際用于特定應用程序運行的代碼路徑和類型的信息。然后,JIT 可以根據這些代碼路徑優(yōu)化代碼,有時會顯著提高性能。我們在測試和生產中都看到了兩位數的健康改進。有一組經典的編譯器技術在沒有PGO 的情況下使用JIT 或提前編譯都無法實現(xiàn)。我們現(xiàn)在能夠應用這些技術。熱/冷分離是一種這樣的技術,而去虛擬化是另一種技術。
要啟用動態(tài)PGO,請在應用程序將運行的環(huán)境中進行設置DOTNET_TieredPGO=1。
如性能部分所述,動態(tài)PGO 將TechEmpower JSON"MVC"套件每秒的請求數提高了26%(510K -> 640K)。這是一個驚人的改進,無需更改代碼。
我們的目標是在未來的.NET 版本中默認啟用動態(tài)PGO,希望在.NET 7 中啟用。我們強烈建議您在應用程序中嘗試動態(tài)PGO 并向我們提供反饋。
完整的 PGO
要充分利用Dynamic PGO,您可以設置兩個額外的環(huán)境變量:DOTNET_TC_QuickJitForLoops=1和DOTNET_ReadyToRun=0。 這確保了盡可能多的方法參與分層編譯。我們將此變體稱為 Full PGO 。與動態(tài)PGO 相比,完整PGO 可以提供更大的穩(wěn)態(tài)性能優(yōu)勢,但啟動時間會更慢(因為必須在第0 層運行更多方法)。
您不希望將此選項用于短期運行的無服務器應用程序,但對于長期運行的應用程序可能有意義。
在未來的版本中,我們計劃精簡和簡化這些選項,以便您可以更簡單地獲得完整PGO 的好處并用于更廣泛的應用程序。
靜態(tài) PGO
我們目前使用 靜態(tài) PGO 來優(yōu)化.NET 庫程序集,例如R2R(Ready To Run)附帶的程序集System.Private.CoreLib。
靜態(tài)PGO 的好處是,在使用crossgen 將程序集編譯為R2R 格式時會進行優(yōu)化。這意味著有運行時的好處而沒有運行時成本。這是非常重要的,也是PGO 對C 很重要的原因,例如。
循環(huán)對齊
內存對齊是現(xiàn)代計算中各種操作的共同要求。在.NET 5 中,我們開始在 32 字節(jié)邊界對齊方法。在.NET 6 中,我們添加了一項執(zhí)行自適應循環(huán)對齊的功能,該功能在具有循環(huán)的方法中添加NOP填充指令,以便循環(huán)代碼從mod(16) 或mod(32) 內存地址開始。這些更改改進并穩(wěn)定了.NET 代碼的性能。
在下面的冒泡排序圖中,數據點1 表示我們開始在32 字節(jié)邊界對齊方法的點。數據點2 表示我們也開始對齊內部循環(huán)的點。如您所見,基準測試的性能和穩(wěn)定性都有很大提高。
硬件加速結構
結構是CLR 類型系統(tǒng)的重要組成部分。近年來,它們經常被用作整個.NET 庫中的性能原語。最近的例子ValueTask是ValueTuple和Span<T>。記錄結構是一個新的例子。在.NET 5 和.NET 6 中,我們一直在提高結構的性能,部分原因是通過確保結構是局部變量、參數或方法的返回值時可以保存在超快速CPU 寄存器中)。這對于使用向量計算的API 特別有用。
穩(wěn)定性能測量
團隊中有大量從未出現(xiàn)在博客上的工程系統(tǒng)工作。這對于您使用的任何硬件或軟件產品都是如此。JIT 團隊開展了一個項目來穩(wěn)定性能測量,目標是增加我們內部性能實驗室自動化自動報告的回歸值。這個項目很有趣,因為需要進行深入調查和產品更改才能實現(xiàn)穩(wěn)定性。它還展示了我們?yōu)楸3趾吞岣呖冃Ф饬康囊?guī)模。
此圖像演示了不穩(wěn)定的性能測量,其中性能在連續(xù)運行中在慢速和快速之間波動。x 軸是測試日期,y 軸是測試時間,以納秒為單位。到圖表末尾(提交這些更改后),您可以看到測量值穩(wěn)定,結果最好。這張圖片展示了一個單一的測試。還有更多測試在dotnet/runtime #43227中被證明具有類似的行為。
即用型代碼 /Crossgen 2
Crossgen2 是crossgen 工具的替代品。它旨在滿足兩個結果:
- 讓crossgen開發(fā)更高效。
- 啟用一組目前無法通過crossgen 實現(xiàn)的功能。
這種轉換有點類似于本機代碼csc.exe 到托管代碼Roslyn 編譯器。Crossgen2 是用C# 編寫的,但是它沒有像Roslyn 那樣公開一個花哨的API。
我們可能已經/已經為.NET 6 和7 計劃了六個項目,這些項目依賴于crossgen2。矢量指令默認提議是我們希望為.NET 6 但更可能是.NET 7 進行的crossgen2 功能和產品更改的一個很好的例子。版本氣泡是另一個很好的例子。
Crossgen2 支持跨操作系統(tǒng)和架構維度的交叉編譯(因此稱為"crossgen")。這意味著您將能夠使用單個構建機器為所有目標生成本機代碼,至少與準備運行的代碼相關。但是,運行和測試該代碼是另一回事,為此您需要合適的硬件和操作系統(tǒng)。
第一步是用crossgen2編譯平臺本身。我們使用.NET 6 完成了所有架構的任務。因此,我們能夠在此版本中淘汰舊的crossgen。請注意,crossgen2 僅適用于CoreCLR,而不適用于基于Mono 的應用程序(它們具有一組單獨的代碼生成工具)。
這個項目——至少一開始——并不以性能為導向。目標是啟用更好的架構來托管RyuJIT(或任何其他)編譯器以離線方式生成代碼(不需要或啟動運行時)。
你可能會說“嘿……如果是用C# 編寫的,難道你不需要啟動運行時來運行crossgen2 嗎?” 是的,但這不是本文中“離線”的含義。當crossgen2 運行時,我們不使用運行crossgen2 的運行時附帶的JIT 來生成準備運行(R2R) 代碼. 那是行不通的,至少對于我們的目標來說是行不通的。想象一下crossgen2 在x64 機器上運行,我們需要為Arm64 生成代碼。Crossgen2 將Arm64 RyuJIT(針對x64 編譯)加載為原生插件,然后使用它生成Arm64 R2R 代碼。機器指令只是保存到文件中的字節(jié)流。它也可以在相反的方向工作。在Arm64 上,crossgen2 可以使用編譯為Arm64 的x64 RyuJIT 生成x64 代碼。我們使用相同的方法來針對x64 機器上的x64 代碼。Crossgen2 會加載一個RyuJIT,它是為任何需要的配置而構建的。這可能看起來很復雜,但如果您想啟用無縫的交叉定位模型,它就是您需要的那種系統(tǒng),而這正是我們想要的。
我們希望只在一個版本中使用術語“crossgen2”,之后它將替換現(xiàn)有的crossgen,然后我們將回到使用術語“crossgen”來表示“crossgen2”。
.NET 診斷:EventPipe
EventPipe 是我們用于在進程內或進程外輸出事件、性能數據和計數器的跨平臺機制。從.NET 6 開始,我們已將實現(xiàn)從C 移至C。通過此更改,Mono 也使用EventPipe。這意味著CoreCLR 和Mono 都使用相同的事件基礎設施,包括.NET 診斷CLI 工具。
這一變化還伴隨著CoreCLR 的小幅減?。?/span>
庫 | 大小之后 – 大小之前 | 差異 |
libcoreclr.so | 7037856 – 7049408 | -11552 |
我們還進行了一些更改,以提高 EventPipe 在負載下的吞吐量。在最初的幾個預覽版中,我們進行了一系列更改,從而使吞吐量提高了.NET 5 的2.06 倍:
對于這個基準,越高越好。.NET 6 是橙色線,.NET 5 是藍色線。
SDK
對.NET SDK 進行了以下改進。
.NET 6 SDK 可選工作負載的 CLI 安裝
.NET 6 引入了SDK 工作負載的概念。工作負載是可選組件,可以安裝在.NET SDK 之上以啟用各種場景。.NET 6 中的新工作負載是:.NET MAUI 和Blazor WebAssembly AOT 工作負載。我們可能會在.NET 7 中創(chuàng)建新的工作負載(可能來自現(xiàn)有的SDK)。工作負載的最大好處是減少大小和可選性。我們希望隨著時間的推移使SDK 變得更小,并且只安裝您需要的組件。這個模型對開發(fā)者機器有好處,對CI 來說甚至更好。
Visual Studio 用戶并不真正需要擔心工作負載。工作負載功能經過專門設計,以便像Visual Studio 這樣的安裝協(xié)調器可以為您安裝工作負載??梢酝ㄟ^CLI 直接管理工作負載。
工作負載功能公開了用于管理工作負載的多個動詞,包括以下幾個:
- dotnet workload restore— 安裝給定項目所需的工作負載。
- dotnet workload install— 安裝命名工作負載。
- dotnet workload list— 列出您已安裝的工作負載。
- dotnet workload update— 將所有已安裝的工作負載更新到最新的可用版本。
update動詞查詢更新nuget.org的工作負載清單、更新本地清單、下載已安裝工作負載的新版本,然后刪除所有舊版本的工作負載。這類似于apt update && apt upgrade -y(用于基于Debian 的Linux 發(fā)行版)。將工作負載視為SDK 的私有包管理器是合理的。它是私有的,因為它僅適用于SDK 組件。我們將來可能會重新考慮這一點。這些dotnet workload命令在給定SDK 的上下文中運行。假設您同時安裝了.NET 6 和.NET 7。工作負載命令將為每個SDK 提供不同的結果,因為工作負載將不同(至少相同工作負載的不同版本)。
請注意,將http://NuGet.org 中的工作負載復制到您的SDK 安裝中,因此如果SDK 安裝位置受到保護(即在管理員/根位置),dotnet workload install則需要運行提升或使用sudo。
內置 SDK 版本檢查
為了更容易跟蹤SDK 和運行時的新版本何時可用,我們向.NET 6 SDK 添加了一個新命令。
dotnet sdk check
它會告訴您是否有可用于您已安裝的任何.NET SDK、運行時或工作負載的更新版本。您可以在下圖中看到新體驗。
dotnet new
您現(xiàn)在可以在http://NuGet.org 中搜索帶有.dotnet new –search
模板安裝的其他改進包括支持切換以支持私有NuGet 源的授權憑據。–interactive
安裝CLI 模板后,您可以通過和檢查更新是否可用。–update-check–update-apply
NuGet 包驗證
包驗證工具使NuGet 庫開發(fā)人員能夠驗證他們的包是否一致且格式正確。
這包括:
- 驗證版本之間沒有重大更改。
- 驗證包對于所有特定于運行時的實現(xiàn)是否具有相同的公共API 集。
- 確定任何目標框架或運行時適用性差距。
該工具是SDK 的一部分。使用它的最簡單方法是在項目文件中設置一個新屬性。
<EnablePackageValidation> true </EnablePackageValidation>
更多 Roslyn 分析儀
在.NET 5 中,我們提供了大約250 個帶有.NET SDK 的分析器。其中許多已經存在,但作為NuGet 包在帶外發(fā)送。我們?yōu)?.NET 6 添加了更多分析器。
默認情況下,大多數新分析器都在信息級別啟用。您可以通過如下配置分析模式在警告級別啟用這些分析器:<AnalysisMode>All</AnalysisMode>
我們?yōu)?NET 6 發(fā)布了我們想要的一組分析器(加上一些附加功能),然后將它們中的大多數做成了可供抓取的。社區(qū)添加了幾個實現(xiàn),包括這些。
感謝Meik Tranel和Newell Clark。
為 Platform Compatibility Analyzer 啟用自定義防護
CA1416 平臺兼容性分析器已經使用OperatingSystem和RuntimeInformation中的方法識別平臺防護,例如OperatingSystem.IsWindows和OperatingSystem.IsWindowsVersionAtLeast。但是,分析器無法識別任何其他保護可能性,例如緩存在字段或屬性中的平臺檢查結果,或者在輔助方法中定義了復雜的平臺檢查邏輯。
為了允許自定義守衛(wèi)的可能性,我們添加了新屬性 SupportedOSPlatformGuard并UnsupportedOSPlatformGuard使用相應的平臺名稱和/或版本注釋自??定義守衛(wèi)成員。此注釋被平臺兼容性分析器的流分析邏輯識別和尊重。
用法
[UnsupportedOSPlatformGuard("browser")] // The platform guard attribute#if TARGET_BROWSER internal bool IsSupported => false;#else internal bool IsSupported => true;#endif [UnsupportedOSPlatform("browser")] void ApiNotSupportedOnBrowser() { } void M1() { ApiNotSupportedOnBrowser(); // Warns: This call site is reachable on all platforms.'ApiNotSupportedOnBrowser()' is unsupported on: 'browser' if (IsSupported) { ApiNotSupportedOnBrowser(); // Not warn } } [SupportedOSPlatform("Windows")] [SupportedOSPlatform("Linux")] void ApiOnlyWorkOnWindowsLinux() { } [SupportedOSPlatformGuard("Linux")] [SupportedOSPlatformGuard("Windows")] private readonly bool _isWindowOrLinux = OperatingSystem.IsLinux() || OperatingSystem.IsWindows(); void M2() { ApiOnlyWorkOnWindowsLinux(); // This call site is reachable on all platforms.'ApiOnlyWorkOnWindowsLinux()' is only supported on: 'Linux', 'Windows'. if (_isWindowOrLinux) { ApiOnlyWorkOnWindowsLinux(); // Not warn } }}
結束
歡迎使用.NET 6。它是另一個巨大的.NET 版本,在性能、功能、可用性和安全性方面都有很多的改進。我們希望您能找到許多改進,最終使您在日常開發(fā)中更有效率和能力,并提高性能或降低生產中應用程序的成本。我們已經開始從那些已經開始使用.NET 6 的人那里聽到好消息。
在Microsoft,我們還處于.NET 6 部署的早期階段,一些關鍵應用程序已經投入生產,未來幾周和幾個月內還會有更多應用程序推出。
.NET 6 是我們最新的LTS 版本。我們鼓勵每個人都轉向它,特別是如果您使用的是.NET 5。我們期待它成為有史以來采用速度最快的.NET 版本。
此版本是至少1000 人(但可能更多)的結果。這包括來自Microsoft 的.NET 團隊以及社區(qū)中的更多人。我試圖在這篇文章中包含許多社區(qū)貢獻的功能。感謝您抽出寶貴時間創(chuàng)建這些內容并完成我們的流程。我希望這次經歷是一次美好的經歷,并且更多的人會做出貢獻。
這篇文章是許多有才華的人合作的結果。貢獻包括團隊在整個發(fā)布過程中提供的功能內容、為此最終帖子創(chuàng)建的重要新內容,以及使最終內容達到您應得的質量所需的大量技術和散文更正。很高興為您制作它和所有其他帖子。
感謝您成為.NET 開發(fā)人員。