迎使用.NET 6。今天的版本是.NET 團隊和社區一年多努力的結果。C# 10 和F# 6 提供了語言改進,使您的代碼更簡單、更好。性能大幅提升,我們已經看到微軟降低了托管云服務的成本。.NET 6 是第一個原生支持Apple Silicon (Arm64) 的版本,并且還針對Windows Arm64 進行了改進。我們構建了一個新的動態配置文件引導優化(PGO) 系統,該系統可提供僅在運行時才可能進行的深度優化。使用dotnet monitor和OpenTelemetry改進了云診斷。WebAssembly支持更有能力和性能。為HTTP/3添加了新的API ,處理JSON、數學和直接操作內存。.NET 6 將支持三年。開發人員已經開始將應用程序升級到.NET 6,我們在生產中聽到了很好的早期成果。.NET 6 已為您的應用程序做好準備。
您可以下載適用于Linux、macOS 和Windows 的.NET 6 。
請參閱ASP.NET Core、Entity Framework、Windows Forms、.NET MAUI、YARP和dotnet 監視器帖子,了解各種場景中的新增功能。
.NET 6 是:
該版本包括大約一萬次git 提交。即使這篇文章很長,它也跳過了許多改進。您必須下載并試用.NET 6 才能看到所有新功能。
.NET 6 是一個長期支持(LTS) 版本,將支持三年。它支持多種操作系統,包括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(現在也是64 位)。Visual Studio Code C# 擴展支持.NET 6 。
Azure App 服務:
注意:如果您的應用已經在應用服務上運行.NET 6 預覽版或RC 版本,則在將.NET 6 運行時和SDK 部署到您所在區域后,它將在第一次重新啟動時自動更新。如果您部署了一個獨立的應用程序,您將需要重新構建和重新部署。
.NET 6 為瀏覽器、云、桌面、物聯網和移動應用程序提供了一個統一的平臺。底層平臺已更新,可滿足所有應用類型的需求,并便于在所有應用中重用代碼。新功能和改進同時適用于所有應用程序,因此您在云或移動設備上運行的代碼的行為方式相同并具有相同的優勢。
.NET 開發人員的范圍隨著每個版本的發布而不斷擴大。機器學習和WebAssembly是最近添加的兩個。例如,通過機器學習,您可以編寫在流數據中查找異常的應用程序。使用WebAssembly,您可以在瀏覽器中托管.NET 應用程序,就像HTML 和JavaScript 一樣,或者將它們與HTML 和JavaScript 混合使用。
最令人興奮的新增功能之一是.NET Multi-platform App UI (.NET MAUI)。您現在可以在單個項目中編寫代碼,從而跨桌面和移動操作系統提供現代客戶端應用程序體驗。.NET MAUI 將比.NET 6 稍晚發布。我們在.NET MAUI 上投入了大量時間和精力,很高興能夠發布它并看到.NET MAUI 應用程序投入生產。
當然,.NET 應用程序也可以在家中使用Windows 桌面(使用Windows Forms和WPF)以及使用ASP.NET Core 在云中。它們是我們提供時間最長的應用程序類型,并且仍然非常受歡迎,我們在.NET 6 中對其進行了改進。
繼續以廣泛平臺為主題,在所有這些操作系統上編寫.NET 代碼很容易。
要以 .NET 6 為目標,您需要使用.NET 6 目標框架,如下所示:
<TargetFramework>net6.0<TargetFramework>
net6.0 Target Framework Moniker (TFM) 使您可以訪問.NET 提供的所有跨平臺API。如果您正在編寫控制臺應用程序、http://ASP.NET Core 應用程序或可重用的跨平臺庫,這是最佳選擇。
如果您針對特定操作系統(例如編寫Windows 窗體或iOS 應用程序),那么還有另一組TFM(每個都針對不言而喻的操作系統)供您使用。它們使您可以訪問所有net6.0的API以及一堆特定于操作系統的API。
每個無版本TFM 都相當于針對.NET 6 支持的最低操作系統版本。如果您想要具體或訪問更新的API,可以指定操作系統版本。
net6.0和net6.0-windows TFMs 都支持(與.NET 5 相同)。Android 和Apple TFM 是.NET 6 的新功能,目前處于預覽階段。稍后的.NET 6 更新將支持它們。
操作系統特定的 TFM 之間沒有兼容性關系。 例如,net6.0-ios與 net6.0-tvos不兼容。 如果您想共享代碼,您需要使用帶有#if 語句的源代碼或帶有net6.0目標代碼的二進制文件來實現。
自從我們啟動.NET Core 項目以來,該團隊一直在不斷地關注性能。Stephen Toub在記錄每個版本的.NET 性能進展方面做得非常出色。歡迎查看在.NET 6 中的性能改進的帖子。在這篇文章中,里面包括您想了解的重大性能改進,包括文件IO、接口轉換、PGO 和System.Text.Json。
動態輪廓引導優化(PGO)可以顯著提高穩態性能。例如,PGO 為TechEmpower JSON"MVC"套件的每秒請求數提高了26%(510K -> 640K)。
動態PGO 建立在分層編譯的基礎上,它使方法能夠首先非常快速地編譯(稱為"第0 層")以提高啟動性能,然后在啟用大量優化的情況下隨后重新編譯(稱為"第1 層")一旦該方法被證明是有影響的。該模型使方法能夠在第0 層中進行檢測,以允許對代碼的執行進行各種觀察。在第1 層重新調整這些方法時,從第0 層執行收集的信息用于更好地優化第1 層代碼。這就是機制的本質。
動態PGO 的啟動時間將比默認運行時稍慢,因為在第0 層方法中運行了額外的代碼來觀察方法行為。
要啟用動態 PGO,請在應用程序將運行的環境中設置 DOTNET_TieredPGO=1。 您還必須確保啟用分層編譯(默認情況下)。 動態 PGO 是可選的,因為它是一種新的且有影響力的技術。 我們希望發布選擇加入使用和相關反饋,以確保它經過全面壓力測試。 我們對分層編譯做了同樣的事情。 至少一個非常大的 Microsoft 服務支持并已在生產中使用動態 PGO。 我們鼓勵您嘗試一下。
您可以在.NET 6中的性能帖子中看到更多關于動態PGO 優勢的信息,包括以下微基準,它測量特定LINQ 枚舉器的成本。
private IEnumerator<long> _source=Enumerable.Range(0, long.MaxValue).GetEnumerator();
[Benchmark]
public void MoveNext()=> _source.MoveNext();
這是有和沒有動態PGO 的結果。
這是一個相當大的差異,但代碼大小也有所增加,這可能會讓一些讀者感到驚訝。這是由JIT 生成的匯編代碼的大小,而不是內存分配(這是一個更常見的焦點)。.NET 6 性能帖子對此有很好的解釋。
PGO 實現中常見的一種優化是"熱/冷分離",其中經常執行的方法部分(“熱”)在方法開始時靠近在一起,而不經常執行的方法部分(“冷”)是移到方法的末尾。這樣可以更好地使用指令緩存,并最大限度地減少可能未使用的代碼負載。
作為上下文,接口調度是 .NET 中最昂貴的調用類型。 非虛擬方法調用是最快的,甚至更快的是可以通過內聯消除的調用。 在這種情況下,動態 PGO 為 MoveNext 提供了兩個(替代)調用站點。 第一個 - 熱的 - 是對 Enumerable+RangeIterator.MoveNext的直接調用,另一個 - 冷的 - 是通過 IEnumerator<int>的虛擬接口調用。 如果大多數時候最熱門的人都被叫到,那將是一個巨大的勝利。
這就是魔法。當 JIT 檢測此方法的第 0 層代碼時,包括檢測此接口調度以跟蹤每次調用時 \_source的具體類型。 JIT 發現每次調用都在一個名為 Enumerable+RangeIterator的類型上,這是一個私有類,用于在 Enumerable實現內部實現 Enumerable.Range。因此,對于第 1 層,JIT 已發出檢查以查看 \_source的類型是否為 Enumerable+RangeIterator:如果不是,則跳轉到我們之前強調的執行正常接口調度的冷部分。但如果是 - 基于分析數據,預計絕大多數時間都是這種情況 - 然后它可以繼續直接調用非虛擬化的 Enumerable+RangeIterator.MoveNext方法。不僅如此,它還認為內聯 MoveNext 方法是有利可圖的。最終效果是生成的匯編代碼有點大,但針對預期最常見的確切場景進行了優化。當我們開始構建動態 PGO 時,這些就是我們想要的那種勝利。
動態PGO 將在RyuJIT 部分再次討論。
FileStream幾乎完全用.NET 6 重寫,重點是提高異步文件IO 性能。在Windows 上,實現不再使用阻塞API,并且可以 快幾倍 !我們還改進了所有平臺上的內存使用。在第一次異步操作(通常分配)之后,我們已經使異步操作 免分配 !此外,我們已經使Windows 和Unix 實現不同的邊緣情況的行為統一(這是可能的)。
這種重寫的性能改進使所有操作系統受益。對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 功能:并發讀取和寫入,以及分散/收集 IO。 針對這些情況,我們為 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!
}
該示例演示:
預分配大小功能提高了性能,因為寫入操作不需要擴展文件,并且文件不太可能被碎片化。這種方法提高了可靠性,因為寫入操作將不再因空間不足而失敗,因為空間已被保留。Scatter/Gather IO API 減少了寫入數據所需的系統調用次數。
界面鑄造性能提高了16% - 38%。這種改進對于C# 與接口之間的模式匹配特別有用。
這張圖表展示了一個有代表性的基準測試的改進規模。
將.NET 運行時的一部分從C++ 遷移到托管C# 的最大優勢之一是它降低了貢獻的障礙。這包括接口轉換,它作為早期的.NET 6 更改移至C#。.NET 生態系統中懂C# 的人比懂C++ 的人多(而且運行時使用具有挑戰性的C++ 模式)。僅僅能夠閱讀構成運行時的一些代碼是培養以各種形式做出貢獻的信心的重要一步。
歸功于 Ben Adams。
我們為System.Text.Json 添加了一個源代碼生成器,它避免了在運行時進行反射和代碼生成的需要,并且可以在構建時生成最佳序列化代碼。序列化程序通常使用非常保守的技術編寫,因為它們必須如此。但是,如果您閱讀自己的序列化源代碼(使用序列化程序),您可以看到明顯的選擇應該是什么,可以使序列化程序在您的特定情況下更加優化。這正是這個新的源生成器所做的。
除了提高性能和減少內存之外,源代碼生成器還生成最適合裝配修整的代碼。這有助于制作更小的應用程序。
序列化POCO是一種非常常見的場景。使用新的源代碼生成器,我們觀察到序列化速度比我們的基準 快1.6倍。
TechEmpower緩存基準測試平臺或框架對來自數據庫的信息進行內存緩存。基準測試的.NET 實現執行緩存數據的JSON 序列化,以便將其作為響應發送到測試工具。
我們觀察到約100K RPS 增益( 增加約40%)。與 MemoryCache 性能改進相結合時,.NET 6 的吞吐量比.NET 5 高50% !
歡迎來到C# 10。C# 10 的一個主要主題是繼續從C# 9 中的頂級語句開始的簡化之旅。新功能從 Program.cs中刪除了更多的儀式,導致程序只有一行。 他們的靈感來自于與沒有 C# 經驗的人(學生、專業開發人員和其他人)交談,并了解什么對他們來說最有效且最直觀。
大多數.NET SDK 模板都已更新,以提供現在可以使用C# 10 實現的更簡單、更簡潔的體驗。我們收到反饋說,有些人不喜歡新模板,因為它們不適合專家,刪除面向對象,刪除在編寫C# 的第一天學習的重要概念,或鼓勵在一個文件中編寫整個程序。客觀地說,這些觀點都不正確。新模型同樣適用于作為專業開發人員的學生。但是,它與.NET 6 之前的C 派生模型不同。
C# 10 中還有其他一些功能和改進,包括記錄結構。
全局using 指令讓您using只需指定一次指令并將其應用于您編譯的每個文件。
以下示例顯示了語法的廣度:
您可以將global using語句放在任何 .cs 文件中,包括在 Program.cs中。
隱式 usings 是一個MSBuild 概念,它會根據SDK自動添加一組指令。例如,控制臺應用程序隱式使用不同于ASP.NET Core。
隱式使用是可選的,并在a 中啟用PropertyGroup:
隱式使用對于現有項目是可選的,但默認包含在新C# 項目中。有關詳細信息,請參閱隱式使用。
文件范圍的命名空間使您能夠聲明整個文件的命名空間,而無需將剩余內容嵌套在{ ...}中. 只允許一個,并且必須在聲明任何類型之前出現。
新語法是單個的一行:
namespace MyNamespace;
class MyClass { ... } // Not indented
這種新語法是三行縮進樣式的替代方案:
namespace MyNamespace
{
class MyClass { ... } // Everything is indented
}
好處是在整個文件位于同一個命名空間中的極其常見的情況下減少縮進。
C# 9 將記錄作為一種特殊的面向值的類形式引入。在C# 10 中,您還可以聲明結構記錄。C# 中的結構已經具有值相等,但記錄結構添加了==運算符和IEquatable<T>的實現,以及基于值的ToString實現:
public record struct Person
{
public string FirstName { get; init; }
public string LastName { get; init; }
}
就像記錄類一樣,記錄結構可以是"位置的",這意味著它們有一個主構造函數,它隱式聲明與參數對應的公共成員:
public record structPerson(stringFirstName,stringLastName);
但是,與記錄類不同,隱式公共成員是_可變的自動實現的屬性_。這樣一來,記錄結構就成為了元組的自然成長故事。例如,如果您有一個返回類型(string FirstName, string LastName),并且您希望將其擴展為命名類型,您可以輕松地聲明相應的位置結構記錄并維護可變語義。
如果你想要一個具有只讀屬性的不可變記錄,你可以聲明整個記錄結構readonly(就像你可以其他結構一樣):
publicreadonly record structPerson(stringFirstName,stringLastName);
C# 10 不僅支持記錄結構,還支持_所有_結構以及匿名類型的with表達式:
var updatedPerson=person with{FirstName="Mary"};
F# 6旨在讓F# 更簡單、更高效。這適用于語言設計、庫和工具。我們對F# 6(及更高版本)的目標是消除語言中讓用戶感到驚訝或阻礙學習F# 的極端情況。我們很高興能與F# 社區合作進行這項持續的努力。
新語法task {…}直接創建一個任務并啟動它。這是 F# 6 中最重要的功能之一,它使異步任務更簡單、性能更高,并且與 C# 和其他 .NET 語言的互操作性更強。以前,創建 .NET 任務需要使用async {…}來創建任務并調用Async.StartImmediateAsTask。
該功能task {…}建立在稱為“可恢復代碼”RFC FS-1087的基礎之上。可恢復代碼是一個核心特性,我們希望在未來使用它來構建其他高性能異步和屈服狀態機。
F# 6 還為庫作者添加了其他性能特性,包括InlineIfLambda 和F#活動模式的未裝箱表示。一個特別顯著的性能改進在于列表和數組表達式的編譯,現在它們的速度提高了 4倍 ,并且調試也更好、更簡單。
F# 6 啟用expr[idx]索引語法。到目前為止,F# 一直使用 expr.[idx] 進行索引。刪除點符號是基于第一次使用 F# 用戶的反復反饋,點的使用與他們期望的標準實踐有不必要的差異。在新代碼中,我們建議系統地使用新的expr[idx]索引語法。作為一個社區,我們都應該切換到這種語法。
F# 社區為使 F# 語言在 F# 6 中更加統一做出了重要改進。其中最重要的是消除了 F# 縮進規則中的一些不一致和限制。使 F# 更加統一的其他設計添加包括添加as圖案;在計算表達式中允許“重載自定義操作”(對 DSL 有用);允許_丟棄use綁定并允許%B在輸出中進行二進制格式化。F# 核心庫添加了用于復制和更新列表、數組和序列的新函數,以及其他NativePtr內在函數。自 2.0 起棄用的 F# 的一些舊功能現在會導致錯誤。其中許多更改更好地使 F# 與您的期望保持一致,從而減少意外。
F# 6 還增加了對 F# 中其他“隱式”和“類型導向”轉換的支持。這意味著更少的顯式向上轉換,并為 .NET 樣式的隱式轉換添加了一流的支持。F# 也進行了調整,以更好地適應使用 64 位整數的數字庫時代,并隱式擴展了 32 位整數。
F# 6 中的工具改進使日常編碼更容易。新的"管道調試"允許您單步執行、設置斷點并檢查 F# 管道語法input |> f1 |> f2 的中間值。陰影值的調試顯示已得到改進,消除了調試時常見的混淆源。F# 工具現在也更高效,F# 編譯器并行執行解析階段。F# IDE 工具也得到了改進。F# 腳本現在更加健壯,允許您通過global.json文件固定使用的 .NET SDK 版本。
Hot Reload 是另一個性能特性,專注于開發人員的生產力。它使您能夠對正在運行的應用程序進行各種代碼編輯,從而縮短您等待應用程序重新構建、重新啟動或重新導航到您在進行代碼更改后所在位置所需的時間。
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文件進行了編輯(如日志中所述),兩者都應用于代碼并在不到半秒的時間內非常快速地反映在瀏覽器中。
使用 Visual Studio 2022 時,只需啟動您的應用程序,進行支持的更改,然后使用新的"熱重載"按鈕(如下圖所示)應用這些更改。您還可以通過同一按鈕上的下拉菜單選擇在保存時應用更改。使用 Visual Studio 2022 時,熱重載可用于多個 .NET 版本,適用于 .NET 5+、.NET Core 和 .NET Framework。例如,您將能夠對按鈕的OnClickEvent處理程序進行代碼隱藏更改。應用程序的Main方法不支持它。
注意:RuntimeInformation.FrameworkDescription中存在一個錯誤,該錯誤將在該圖像中展示,很快就會修復。
Hot Reload 還與現有的 Edit and Continue 功能(在斷點處停止時)以及用于實時編輯應用程序 UI 的 XAML Hot Reload 協同工作。目前支持 C# 和 Visual Basic 應用程序(不是 F#)。
.NET 6 中的安全性得到了顯著改進。它始終是團隊關注的重點,包括威脅建模、加密和深度防御防御。
在 Linux 上,我們依賴OpenSSL進行所有加密操作,包括 TLS(HTTPS 必需)。在 macOS 和 Windows 上,我們依賴操作系統提供的功能來實現相同的目的。對于每個新版本的 .NET,我們經常需要添加對新版本 OpenSSL 的支持。.NET 6 增加了對OpenSSL 3的支持。
OpenSSL 3 的最大變化是改進的FIPS 140-2模塊和更簡單的許可。
.NET 6 需要 OpenSSL 1.1 或更高版本,并且會更喜歡它可以找到的最高安裝版本的 OpenSSL,直到并包括 v3。在一般情況下,當您使用的 Linux 發行版默認切換到 OpenSSL 3 時,您最有可能開始使用 OpenSSL 3。大多數發行版還沒有這樣做。例如,如果您在 Red Hat 8 或 Ubuntu 20.04 上安裝 .NET 6,您將不會(在撰寫本文時)開始使用 OpenSSL 3。
OpenSSL 3、Windows 10 21H1 和 Windows Server 2022 都支持ChaCha20Poly1305。您可以在.NET 6 中使用這種新的經過身份驗證的加密方案(假設您的環境支持它)。
感謝 Kevin Jones對 ChaCha20Poly1305 的 Linux 支持。
我們還發布了新的運行時安全緩解路線圖。重要的是,您使用的運行時不受教科書攻擊類型的影響。我們正在滿足這一需求。在 .NET 6 中,我們構建了W^X和英特爾控制流強制技術(CET)的初始實現。W^X 完全受支持,默認為 macOS Arm64 啟用,并且可以選擇加入其他環境。CET 是所有環境的選擇加入和預覽。我們希望在 .NET 7 中的所有環境中默認啟用這兩種技術。
這些天來,對于筆記本電腦、云硬件和其他設備來說,Arm64 令人興奮不已。我們對 .NET 團隊感到同樣興奮,并正在盡最大努力跟上這一行業趨勢。我們直接與 Arm Holdings、Apple 和 Microsoft 的工程師合作,以確保我們的實施是正確和優化的,并且我們的計劃保持一致。這些密切的合作伙伴關系對我們幫助很大。
在此之前,我們通過 .NET Core 3.0 和 Arm32 添加了對 Arm64 的初始支持。該團隊在最近的幾個版本中都對 Arm64 進行了重大投資,并且在可預見的未來這將繼續下去。在 .NET 6 中,我們主要關注在 macOS 和 Windows Arm64 操作系統上支持新的 Apple Silicon 芯片和x64 仿真場景。
您可以在 macOS 11+ 和 Windows 11+ Arm64 操作系統上安裝 Arm64 和 x64 版本的 .NET。我們必須做出多種設計選擇和產品更改以確保其奏效。
我們的策略是“親原生架構”。我們建議您始終使用與原生架構相匹配的 SDK,即 macOS 和 Windows Arm64 上的 Arm64 SDK。SDK 是大量的軟件。在 Arm64 芯片上本地運行的性能將比仿真高得多。我們更新了 CLI 以簡化操作。我們永遠不會專注于優化模擬 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 架構中的最后一個進程創建,然后將其作為您請求的芯片架構(如 x64)啟動。這就是您的代碼運行的過程。這樣,作為開發人員,您可以獲得 Arm64 的好處,但您的代碼可以在它需要的過程中運行。這僅在您需要將某些代碼作為 x64 運行時才相關。如果你不這樣做,那么你可以一直以 Arm64 的方式運行所有東西,這很棒。
對于 macOS 和 Windows Arm64,以下是您需要了解的要點:
有關更多完整信息,請參閱.NET 對macOS 和Windows Arm64的支持。
此討論中缺少Linux。它不像macOS 和Windows 那樣支持x64 仿真。因此,這些新的CLI 特性和支持方法并不直接適用于Linux,Linux 也不需要它們。
我們有一個簡單的工具來演示.NET 運行的環境。
C:Usersrich>dotnet tool install -g dotnet-runtimeinfo
You can invoke the tool using the following command: dotnet-runtimeinfo
Tool '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, MM42MMM
a8" `Y42 a8" "8a 42 42P' `"8a a8P_____42 42
8b 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 information
Version: 6.0.0
FrameworkDescription: .NET 6.0.0-rtm.21522.10
Libraries version: 6.0.0-rtm.21522.10
Libraries hash: 4822e3c3aa77eb82b2fb33c9321f923cf11ddde6
**Environment information
ProcessorCount: 8
OSArchitecture: Arm64
OSDescription: Microsoft Windows 10.0.22494
OSVersion: Microsoft Windows NT 10.0.22494.0
如您所見,該工具在Windows Arm64 上本機運行。我將向您展示http://ASP.NET Core 的樣子。
您可以看到在macOS Arm64 上的體驗是相似的,并且還展示了架構目標。
rich@MacBook-Air app % dotnet --version
6.0.100
rich@MacBook-Air app % dotnet --info | grep RID
RID: osx-arm64
rich@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 run
Hello, Arm64 from .NET 6.0.0-rtm.21522.10!
rich@MacBook-Air app % dotnet run -a x64
Hello, X64 from .NET 6.0.0-rtm.21522.10!
rich@MacBook-Air app %
這張圖片展示了Arm64 執行是Arm64 SDK 的默認設置,以及使用-a參數在目標Arm64 和x64 之間切換是多么容易。完全相同的體驗適用于Windows Arm64。
此圖像演示了相同的內容,但使用的是http://ASP.NET Core。我正在使用與您在上圖中看到的相同的.NET 6 Arm64 SDK。
Docker 支持在本機架構和仿真中運行的容器,本機架構是默認的。這看起來很明顯,但當大多數Docker Hub 目錄都是面向x64 時,這可能會讓人感到困惑。您可以使用-platform linux/amd64來請求x64 圖像。
我們僅支持在Arm64 操作系統上運行Linux Arm64 .NET 容器映像。這是因為我們從不支持在QEMU中運行.NET ,這是Docker 用于架構模擬的。看來這可能是由于 QEMU 的限制。
此圖像演示了我們維護的控制臺示例:mcr.microsoft.com/dotnet/samples。 這是一個有趣的示例,因為它包含一些基本邏輯,用于打印您可以使用的CPU 和內存限制信息。我展示的圖像設置了CPU 和內存限制。
自己試試吧:docker run --rm mcr.microsoft.com/dotnet/samples
Apple Silicon 和x64 仿真支持項目非常重要,但是,我們也普遍提高了Arm64 性能。
此圖像演示了將堆棧幀的內容清零的改進,這是一種常見的操作。綠線是新行為,而橙色線是另一個(不太有益的)實驗,兩者都相對于基線有所改善,由藍線表示。對于此測試,越低越好。
.NET 6 更適合容器,主要基于本文中討論的所有改進,適用于Arm64 和x64。我們還進行了有助于各種場景的關鍵更改。使用.NET 6 驗證容器改進演示了其中一些改進正在一起測試。
Windows 容器改進和新環境變量也包含在11 月9 日(明天)發布的11 月.NET Framework 4.8 容器更新中。
發布說明可在我們的docker 存儲庫中找到:
Windows 容器
.NET 6 增加了對Windows 進程隔離容器的支持。如果您在 Azure Kubernetes 服務(AKS) 中使用Windows 容器,那么您依賴于進程隔離的容器。進程隔離容器可以被認為與Linux 容器非常相似。Linux 容器使用cgroups,Windows 進程隔離容器使用Job Objects。Windows 還提供Hyper-V 容器,通過更強大的虛擬化提供更大的隔離。Hyper-V 容器的.NET 6 沒有任何變化。
此更改的主要價值是現在Environment.ProcessorCount將使用Windows 進程隔離容器報告正確的值。如果在64 核機器上創建2 核容器,Environment.ProcessorCount將返回2. 在以前的版本中,此屬性將報告機器上的處理器總數,與Docker CLI、Kubernetes 或其他容器編排器/運行時指定的限制無關。此值被.NET 的各個部分用于擴展目的,包括.NET 垃圾收集器(盡管它依賴于相關的較低級別的API)。社區庫也依賴此API 進行擴展。
我們最近在AKS 上使用大量pod 在生產中的Windows 容器上與客戶驗證了這一新功能。他們能夠以50% 的內存(與他們的典型配置相比)成功運行,這是以前導致異常的OutOfMemoryException水平StackOverflowException。他們沒有花時間找到最低內存配置,但我們猜測它明顯低于他們典型內存配置的50%。由于這一變化,他們將轉向更便宜的Azure 配置,從而節省資金。只需升級即可,這是一個不錯的、輕松的勝利。
優化縮放
我們從用戶那里聽說,某些應用程序在Environment.ProcessorCount報告正確的值時無法實現最佳擴展。如果這聽起來與您剛剛閱讀的有關Windows 容器的內容相反,那么它有點像。.NET 6 現在提供DOTNET_PROCESSOR_COUNT 環境變量來手動控制Environment.ProcessorCount的值。在典型的用例中,應用程序可能在64 核機器上配置為4核,并且在8或16核方面擴展得最好。此環境變量可用于啟用該縮放。
這個模型可能看起來很奇怪,其中Environment.ProcessorCount和--cpus(通過Docker CLI)值可能不同。默認情況下,容器運行時面向核心等價物,而不是實際核心。這意味著,當你說你想要4 個核心時,你得到的CPU 時間與4 個核心相當,但你的應用程序可能(理論上)在更多的核心上運行,甚至在短時間內在64 核機器上運行所有64 個核心。這可能使您的應用程序能夠在超過4 個線程上更好地擴展(繼續示例),并且分配更多可能是有益的。這假定線程分配基于 Environment.ProcessorCount的值。如果您選擇設置更高的值,您的應用程序可能會使用更多內存。對于某些工作負載,這是一個簡單的權衡。至少,這是一個您可以測試的新選項。
Linux 和Windows 容器均支持此新功能。
Docker 還提供了一個CPU 組功能,您的應用程序可以關聯到特定的內核。在這種情況下不建議使用此功能,因為應用程序可以訪問的內核數量是具體定義的。我們還看到了將它與Hyper-V 容器一起使用時的一些問題,并且它并不是真正適用于那種隔離模式。
Debian 11 "bullseye"
我們密切關注Linux 發行版的生命周期和發布計劃,并嘗試代表您做出最佳選擇。Debian 是我們用于默認Linux 映像的Linux 發行版。如果您6.0從我們的一個容器存儲庫中提取標簽,您將提取一個Debian 映像(假設您使用的是Linux 容器)。對于每個新的.NET 版本,我們都會考慮是否應該采用新的Debian 版本。
作為一項政策,我們不會為了方便標簽而更改Debian 版本,例如6.0, mid-release。如果我們這樣做了,某些應用程序肯定會崩潰。這意味著,在發布開始時選擇Debian 版本非常重要。此外,這些圖像得到了很多使用,主要是因為它們是"好標簽"的引用。
Debian 和.NET 版本自然不會一起計劃。當我們開始.NET 6 時,我們看到Debian "bullseye" 可能會在2021 年發布。我們決定從發布開始就押注于Bullseye。我們開始使用.NET 6 Preview 1發布基于靶心的容器映像,并決定不再回頭。賭注是.NET 6 版本會輸掉與靶心版本的競爭。到8 月8 日,我們仍然不知道Bullseye 什么時候發貨,距離我們自己的版本發布還有三個月,即11 月8 日。我們不想在預覽版Linux 上發布生產.NET 6,但我們堅持我們會輸掉這場競賽的計劃很晚。
當Debian 11 "bullseye"于8 月14 日發布時,我們感到非常驚喜。我們輸掉了比賽,但贏得了賭注。這意味著默認情況下,.NET 6 用戶從第一天開始就可以獲得最佳和最新的Debian。我們相信Debian 11 和.NET 6 將是許多用戶的絕佳組合。抱歉,克星,我們中了靶心。
較新的發行版在其軟件包提要中包含各種軟件包的較新主要版本,并且通常可以更快地獲得CVE 修復。這是對較新內核的補充。新發行版可以更好地為用戶服務。
再往前看,我們很快就會開始計劃對Ubuntu 22.04的支持。Ubuntu是另一個Debian 系列發行版,深受.NET 開發人員的歡迎。我們希望為新的Ubuntu LTS 版本提供當日支持。
向Tianon Gravi 致敬,感謝他們為社區維護Debian 映像并在我們有問題時幫助我們。
Dotnet Monitor
dotnet monitor是容器的重要診斷工具。它作為 sidecar 容器鏡像已經有一段時間了,但處于不受支持的"實驗"狀態。作為.NET 6 的一部分,我們正在發布一個基于.NET 6 的dotnet monitor映像,該映像在生產中得到完全支持。
dotnet monitor已被Azure App Service 用作其http://ASP.NET Core Linux 診斷體驗的實現細節。這是預期的場景之一,建立在dotnet monitor 之上,以提供更高級別和更高價值的體驗。
您現在可以拉取新圖像:
docker pull mcr.microsoft.com/dotnet/monitor:6.0
dotnet monitor使從.NET 進程訪問診斷信息(日志、跟蹤、進程轉儲)變得更加容易。在臺式機上訪問所需的所有診斷信息很容易,但是,這些熟悉的技術在使用容器的生產環境中可能不起作用。dotnet monitor提供了一種統一的方式來收集這些診斷工件,無論是在您的桌面計算機上還是在Kubernetes 集群中運行。收集這些診斷工件有兩種不同的機制:
dotnet monitor為.NET 應用程序提供了一個通用的診斷API,可以使用任何工具在任何地方工作。“通用API”不是.NET API,而是您可以調用和查詢的Web API。dotnet monitor包括一個ASP.NET Web 服務器,它直接與.NET 運行時中的診斷服務器交互并公開來自診斷服務器的數據設計dotnet monitor可實現生產中的高性能監控和安全使用,以控制對特權信息的訪問。dotnet monitor通過非Internet 可尋址的unix domain socket與運行時交互——跨越容器邊界。該模型通信模型非常適合此用例。
結構化 JSON 日志
JSON 格式化程序現在是aspnet.NET 6 容器映像中的默認控制臺記錄器。.NET 5 中的默認設置為簡單的控制臺格式化程序。進行此更改是為了使默認配置與依賴機器可讀格式(如JSON)的自動化工具一起使用。
圖像的輸出現在如下所示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可以通過設置或取消設置環境變量或通過代碼更改來更改記錄器格式類型(有關更多詳細信息,請參閱控制臺日志格式)。
更改后,您將看到如下輸出(就像.NET 5 一樣):
$ docker run --rm -it -p 8000:80 -e Logging__Console__FormatterName="" mcr.microsoft.com/dotnet/samples:aspnetapp
warn: 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://[::]:80
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
Content root path: /app
注意:此更改不會影響開發人員計算機上的.NET SDK,例如dotnet run.此更改特定于aspnet容器映像。
作為我們關注可觀察性的一部分,我們一直在為最后幾個.NET 版本添加對 OpenTelemetry 的支持。在.NET 6 中,我們添加了對OpenTelemetry Metrics API的支持。通過添加對OpenTelemetry 的支持,您的應用程序可以與其他OpenTelemetry系統無縫互操作。
System.Diagnostics.Metrics是OpenTelemetry Metrics API 規范的.NET 實現。Metrics API 是專門為處理原始測量而設計的,目的是高效、同時地生成這些測量的連續摘要。
API 包括Meter可用于創建儀器對象的類。API 公開了四個工具類:Counter、Histogram、ObservableCounter和,ObservableGauge以支持不同的度量方案。此外,API 公開MeterListener該類以允許收聽儀器記錄的測量值,以用于聚合和分組目的。
OpenTelemetry .NET 實現將被擴展以使用這些新的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 窗體中進行重要改進。.NET 6 包括更好的控件可訪問性、設置應用程序范圍的默認字體、模板更新等的能力。
可訪問性改進
在此版本中,我們添加了用于CheckedListBox、LinkLabel、Panel、ScrollBar和TabControlTrackBar的UIA 提供程序,它們使講述人等工具和測試自動化能夠與應用程序的元素進行交互。
默認字體
您現在可以使用.Application.SetDefaultFont
voidApplication.SetDefaultFont(Font font)
最小的應用程序
以下是帶有 .NET 6 的最小Windows 窗體應用程序:
class Program
{
[STAThread]
static void Main()
{
ApplicationConfiguration.Initialize();
Application.Run(new Form1());
}
}
作為.NET 6 版本的一部分,我們一直在更新大多數模板,使其更加現代和簡約,包括Windows 窗體。我們決定讓Windows 窗體模板更傳統一些,部分原因是需要將[STAThread]屬性應用于應用程序入口點。然而,還有更多的戲劇而不是立即出現在眼前。
ApplicationConfiguration.Initialize()是一個源生成API,它在后臺發出以下調用:
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
現在您可以構建通用設計器(例如,報表設計器),因為.NET 6 具有設計器和與設計器相關的基礎架構所缺少的所有部分。有關詳細信息,請參閱此博客文章。
在.NET 6中,已為 Windows 和macOS 啟用內存中單文件應用程序。在.NET 5 中,這種部署類型僅限于 Linux。您現在可以為所有受支持的操作系統發布作為單個文件部署和啟動的單文件二進制文件。單文件應用不再將任何核心運行時程序集提取到臨時目錄。
這種擴展功能基于稱為"超級主機"的構建塊。"apphost" 是在非單文件情況下啟動應用程序的可執行文件,例如myapp.exe或./myapp. Apphost 包含用于查找運行時、加載它并使用該運行時啟動您的應用程序的代碼。Superhost 仍然執行其中一些任務,但使用所有CoreCLR 本機二進制文件的靜態鏈接副本。靜態鏈接是我們用來實現單一文件體驗的方法。本機依賴項(如NuGet 包附帶的)是單文件嵌入的顯著例外。默認情況下,它們不包含在單個文件中。例如,WPF 本機依賴項不是超級主機的一部分,因此會在單文件應用程序之外產生其他文件。您可以使用該設置IncludeNativeLibrariesForSelfExtract嵌入和提取本機依賴項。
靜態分析
我們改進了單文件分析器以允許自定義警告。如果您的API 在單文件發布中不起作用,您現在可以使用[RequiresAssemblyFiles]屬性對其進行標記,如果啟用了分析器,則會出現警告。添加該屬性還將使方法中與單個文件相關的所有警告靜音,因此您可以使用該警告將警告向上傳播到您的公共API。
當 PublishSingleFile 設置為true 時,會自動為exe 項目啟用單文件分析器,但您也可以通過將 EnableSingleFileAnalysis 設置為true 來為任何項目啟用它。 如果您想支持將庫作為單個文件應用程序的一部分,這將很有幫助。
在.NET 5 中,我們為單文件包中行為不同的Assembly.Location和一些其他API添加了警告。
壓縮
單文件包現在支持壓縮,可以通過將屬性設置EnableCompressionInSingleFile為true. 在運行時,文件會根據需要解壓縮到內存中。壓縮可以為某些場景節省大量空間。
讓我們看一下與NuGet 包資源管理器一起使用的單個文件發布(帶壓縮和不帶壓縮)。
無壓縮: 172 MB
壓縮: 71.6 MB
壓縮會顯著增加應用程序的啟動時間,尤其是在Unix 平臺上。Unix 平臺有一個不能用于壓縮的無拷貝快速啟動路徑。您應該在啟用壓縮后測試您的應用程序,看看額外的啟動成本是否可以接受。
單文件調試
目前只能使用平臺調試器(如WinDBG)來調試單文件應用程序。我們正在考慮使用更高版本的Visual Studio 2022 添加Visual Studio 調試。
macOS 上的單文件簽名
單文件應用程序現在滿足macOS 上的Apple 公證和簽名要求。具體更改與我們根據離散文件布局構建單文件應用程序的方式有關。
Apple 開始對macOS Catalina 實施新的簽名和公證要求。我們一直在與Apple 密切合作,以了解需求,并尋找使.NET 等開發平臺能夠在該環境中正常工作的解決方案。我們已經進行了產品更改并記錄了用戶工作流程,以滿足Apple 在最近幾個.NET 版本中的要求。剩下的差距之一是單文件簽名,這是在macOS 上分發.NET 應用程序的要求,包括在macOS 商店中。
該團隊一直致力于為多個版本進行IL 修整。.NET 6 代表了這一旅程向前邁出的重要一步。我們一直在努力使更激進的修剪模式安全且可預測,因此有信心將其設為默認模式。TrimMode=link以前是可選功能,現在是默認功能。
我們有一個三管齊下的修剪策略:
由于使用未注釋反射的應用程序的結果不可靠,修剪之前一直處于預覽狀態。有了修剪警告,體驗現在應該是可預測的。沒有修剪警告的應用程序應該正確修剪并且在運行時觀察到行為沒有變化。目前,只有核心的.NET 庫已經完全注解了修剪,但我們希望看到生態系統注釋修剪并兼容修剪
讓我們使用SDK 工具之一的crossgen來看看這個修剪改進。它可以通過幾個修剪警告進行修剪,crossgen 團隊能夠解決。
首先,讓我們看一下將crossgen 發布為一個獨立的應用程序而無需修剪。它是80 MB(包括.NET 運行時和所有庫)。
然后我們可以嘗試(現在是舊版).NET 5 默認修剪模式,copyused. 結果降至55 MB。
新的.NET 6 默認修剪模式link將獨立文件大小進一步降低到36MB。
我們希望新的link修剪模式能更好地與修剪的期望保持一致:顯著節省和可預測的結果。
默認啟用警告
修剪警告告訴您修剪可能會刪除運行時使用的代碼的地方。這些警告以前默認禁用,因為警告非常嘈雜,主要是由于 .NET 平臺沒有參與修剪作為第一類場景。
我們對大部分 .NET 庫進行了注釋,以便它們產生準確的修剪警告。因此,我們覺得是時候默認啟用修剪警告了。http://ASP.NET Core 和 Windows 桌面運行時庫尚未注釋。我們計劃接下來注釋 http://ASP.NET 服務組件(在 .NET 6 之后)。我們希望看到社區在 .NET 6 發布后對 NuGet 庫進行注釋。
您可以通過設置<SuppressTrimAnalysisWarnings>為true來禁用警告。
更多信息:
與本機 AOT 共享
我們也為Native AOT實驗實現了相同的修剪警告,這應該會以幾乎相同的方式改善 Native AOT 編譯體驗。
數學
我們顯著改進了數學 API。社區中的一些人已經在享受這些改進。
面向性能的 API
System.Math 中添加了面向性能的數學 API。如果底層硬件支持,它們的實現是硬件加速的。
新 API:
新的重載:
性能改進:
大整數性能
改進了從十進制和十六進制字符串中解析 BigIntegers。我們看到了高達89% 的改進,如下圖所示(越低越好)。
感謝約瑟夫·達席爾瓦。
Complex API 現在注釋為 readonly
現在對各種API 進行了注釋,System.Numerics.Complexreadonly以確保不會對readonly值或傳遞的值進行復制in。
歸功于hrrrrustic 。
BitConverter 現在支持浮點到無符號整數位廣播
BitConverter 現在支持DoubleToUInt64Bits, HalfToUInt16Bits, SingleToUInt32Bits, UInt16BitsToHalf, UInt32BitsToSingle, 和UInt64BitsToDouble. 這應該使得在需要時更容易進行浮點位操作。
歸功于Michal Petryka 。
BitOperations 支持附加功能
BitOperations現在支持IsPow2,RoundUpToPowerOf2和提供nint/nuint重載現有函數。
感謝約翰凱利、霍耀源和羅賓林德納。
Vector現在支持C# 9 中添加的原始類型nint和nuint原始類型。例如,此更改應該可以更簡單地使用帶有指針或平臺相關長度類型的SIMD 指令。
Vector現在支持一種Sum方法來簡化計算向量中所有元素的“水平和”的需要。歸功于伊萬茲拉塔諾夫。
Vector現在支持一種通用方法As<TFrom, TTo>來簡化在具體類型未知的通用上下文中處理向量。感謝霍耀源
重載支持Span已添加到Vector2、Vector3和Vector4以改善需要加載或存儲矢量類型時的體驗。
更好地解析標準數字格式
我們改進了標準數字類型的解析器,特別是.ToString和.TryFormatParse。他們現在將理解對精度 >99 位小數的要求,并將為那么多位數提供準確的結果。此外,解析器現在更好地支持方法中的尾隨零。
以下示例演示了之前和之后的行為。
System.Text.Json提供多種高性能API 用于處理JSON 文檔。在過去的幾個版本中,我們添加了新功能,以進一步提高JSON 處理性能并減輕對希望從NewtonSoft.Json遷移的人的阻礙。 此版本包括在該路徑上的繼續,并且在性能方面向前邁出了一大步,特別是在序列化程序源生成器方面。
注意:使用.NET 6 RC1 或更早版本的源代碼生成的應用程序應重新編譯。
幾乎所有.NET 序列化程序的支柱都是反射。反射對于某些場景來說是一種很好的能力,但不能作為高性能云原生應用程序(通常(反)序列化和處理大量JSON 文檔)的基礎。反射是啟動、內存使用和程序集修整的問題。
運行時反射的替代方法是編譯時源代碼生成。在.NET 6 中,我們包含一個新的源代碼生成器作為 System.Text.Json. JSON 源代碼生成器可以與多種方式結合使用JsonSerializer并且可以通過多種方式進行配置。
它可以提供以下好處:
默認情況下,JSON 源生成器為給定的可序列化類型發出序列化邏輯。JsonSerializer通過生成直接使用的源代碼,這提供了比使用現有方法更高的性能Utf8JsonWriter。簡而言之,源代碼生成器提供了一種在編譯時為您提供不同實現的方法,以使運行時體驗更好。
給定一個簡單的類型:
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!"}
最快和最優化的源代碼生成模式——基于Utf8JsonWriter——目前僅可用于序列化。Utf8JsonReader根據您的反饋,將來可能會提供對反序列化的類似支持。
源生成器還發出類型元數據初始化邏輯,這也有利于反序列化。JsonMessage要反序列化使用預生成類型元數據的實例,您可以執行以下操作:
JsonSerializer.Deserialize(json, JsonContext.Default.JsonMessage);
您現在可以使用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>。我們為此添加了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 數組讀取,盡管將來可能會根據反饋放寬。
現有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 值。
可寫JSON DOM 特性為System.Text.Json添加了一個新的簡單且高性能的編程模型。這個新的API 很有吸引力,因為它避免了需要強類型的序列化合約,并且與現有的JsonDocument類型相比,DOM 是可變的。
這個新的 API 有以下好處:
以下示例演示了新的編程模型。
// 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 chained
Debug.Assert(jObject["MyChildObject"]["MyArray"][1].GetValue<int>()==11);
JsonSerializer(System.Text.Json)現在支持在序列化對象圖時忽略循環的能力。該ReferenceHandler.IgnoreCycles選項具有與Newtonsoft.Json ReferenceLoopHandling.Ignore類似的行為。一個關鍵區別是System.Text.Json 實現用null JSON 標記替換引用循環,而不是忽略對象引用。
您可以在以下示例中看到ReferenceHandler.IgnoreCycles的行為。在這種情況下,該Next屬性被序列化為null,因為否則它會創建一個循環。
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 。讓我解釋一下為什么這個項目很重要。
源代碼構建是一個場景,也是我們在發布.NET Core 1.0 之前一直與Red Hat 合作開發的基礎架構。幾年后,我們非常接近于交付它的全自動版本。對于Red Hat Enterprise Linux (RHEL) .NET 用戶來說,這個功能很重要。Red Hat 告訴我們,.NET 已經發展成為其生態系統的重要開發者平臺。好的!
Linux 發行版的黃金標準是使用作為發行版存檔一部分的編譯器和工具鏈構建開源代碼。這適用于.NET 運行時(用C++ 編寫),但不適用于任何用C# 編寫的代碼。對于C# 代碼,我們使用兩遍構建機制來滿足發行版要求。這有點復雜,但了解流程很重要。
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 來構建每月更新。
這個過程可能令人驚訝和困惑。開源發行版需要通過開源工具構建。此模式確保不需要Microsoft 構建的SDK,無論是有意還是無意。作為開發者平臺,包含在發行版中的門檻比僅使用兼容許可證的門檻更高。源代碼構建項目使.NET 能夠滿足該標準。
源代碼構建的可交付成果是源代碼壓縮包。源tarball 包含SDK 的所有源(對于給定版本)。從那里,紅帽(或其他組織)可以構建自己的SDK 版本。Red Hat 政策要求使用內置源工具鏈來生成二進制tar 球,這就是他們使用兩遍方法的原因。但是源代碼構建本身不需要這種兩遍方法。
在Linux 生態系統中,給定組件同時擁有源和二進制包或tarball 是很常見的。我們已經有了可用的二進制tarball,現在也有了源tarball。這使得.NET 與標準組件模式相匹配。
.NET 6 的重大改進是源tarball 現在是我們構建的產品。它過去需要大量的人工來制作,這也導致將源tarball 交付給Red Hat 的延遲很長。雙方都對此不滿意。
在這個項目上,我們與紅帽密切合作五年多。它的成功在很大程度上要歸功于我們有幸與之共事的優秀紅帽工程師的努力。其他發行版和組織已經并將從他們的努力中受益。
附帶說明一下,源代碼構建是朝著可重現構建邁出的一大步,我們也堅信這一點。.NET SDK 和C# 編譯器具有重要的可重現構建功能。
除了已經涵蓋的API 之外,還添加了以下API。
壓縮對于通過網絡傳輸的任何數據都很重要。WebSockets 現在啟用壓縮。我們使用了WebSockets 的擴展permessage-deflate實現,RFC 7692。它允許使用該DEFLATE算法壓縮WebSockets 消息負載。此功能是GitHub 上Networking 的主要用戶請求之一。
與加密一起使用的壓縮可能會導致攻擊,例如CRIME和BREACH。這意味著不能在單個壓縮上下文中將秘密與用戶生成的數據一起發送,否則可以提取該秘密。為了讓用戶注意到這些影響并幫助他們權衡風險,我們將其中一個關鍵API 命名為DangerousDeflateOptions。我們還添加了關閉特定消息壓縮的功能,因此如果用戶想要發送秘密,他們可以在不壓縮的情況下安全地執行此操作。
禁用壓縮時WebSocket的內存占用減少了約27%。
從客戶端啟用壓縮很容易,如下例所示。但是,請記住,服務器可以協商設置,例如請求更小的窗口或完全拒絕壓縮。
var cws=new ClientWebSocket();
cws.Options.DangerousDeflateOptions=new WebSocketDeflateOptions()
{
ClientMaxWindowBits=10,
ServerMaxWindowBits=10
};
還添加了對 ASP.NET Core 的 WebSocket 壓縮支持。
歸功于伊萬茲拉塔諾夫。
SOCKS是一種代理服務器實現,可以處理任何TCP 或UDP 流量,使其成為一個非常通用的系統。這是一個長期存在的社區請求,已添加到.NET 6中。
此更改增加了對Socks4、Socks4a 和Socks5 的支持。例如,它可以通過SSH 測試外部連接或連接到 Tor 網絡。
該類WebProxy現在接受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創建API是為了處理服務的處置IAsyncDisposable。以前,您可能已經注意到處置IAsyncDisposable服務提供者可能會引發InvalidOperationException異常。
以下示例演示了新模式,CreateAsyncScope用于啟用using語句的安全使用。
await using (var scope=provider.CreateAsyncScope())
{
var foo=scope.ServiceProvider.GetRequiredService<Foo>();
}
以下示例演示了現有的問題案例:
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
await using var provider=new ServiceCollection()
.AddScoped<Foo>()
.BuildServiceProvider();
// This using can throw InvalidOperationException
using (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。源生成日志支持旨在為現代.NET 應用程序提供高度可用和高性能的日志解決方案。自動生成的源代碼依賴于ILogger接口和LoggerMessage.Define功能。
LoggerMessageAttribute源生成器在用于partial日志記錄方法時觸發。當被觸發時,它要么能夠自動生成partial它正在裝飾的方法的實現,要么生成編譯時診斷,并提供有關正確使用的提示。編譯時日志記錄解決方案在運行時通常比現有的日志記錄方法快得多。它通過最大限度地消除裝箱、臨時分配和副本來實現這一點。
與直接手動使用LoggerMessage.Define API相比,有以下好處:
要使用LoggerMessageAttribute,消費類和方法需要是partial。代碼生成器在編譯時觸發并生成partial方法的實現。
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,并且在屬性定義中指定了日志級別。在靜態上下文中使用屬性時,ILogger需要實例作為參數。您也可以選擇在非靜態上下文中使用該屬性。有關更多示例和使用場景,請訪問編譯時日志記錄源生成器文檔。
System.Linq — 可枚舉的支持 Index 和 Range 參數
該Enumerable.ElementAt方法現在接受來自可枚舉末尾的索引,如以下示例所示。
Enumerable.Range(1, 10).ElementAt(^2); // returns 9
添加了一個Enumerable.Take接受Range參數的重載。它簡化了對可枚舉序列的切片:
感謝@dixin 。
System.Linq — TryGetNonEnumeratedCount
該TryGetNonEnumeratedCount方法嘗試在不強制枚舉的情況下獲取源可枚舉的計數。這種方法在枚舉之前預分配緩沖區很有用的場景中很有用,如下面的示例所示。
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檢查實現ICollection/ ICollection<T>;或利用Linq 采用的一些內部優化的源。
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
如果源可枚舉為空,則現有的FirstOrDefault /LastOrDefault /SingleOrDefault方法返回default(T)。添加了新的重載,它們接受在這種情況下返回的默認參數,如以下示例所示。
Enumerable.Empty\<int\>().SingleOrDefault(-1); // returns -1
感謝@ Foxtrek64 。
System.Linq — Zip 接受三個可枚舉的重載
Zip方法現在支持組合三個枚舉,如以下示例所示。
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。
優先隊列
PriorityQueue<TElement, TPriority>(System.Collections.Generic) 是一個新集合,可以添加具有值和優先級的新項目。在出隊時,PriorityQueue 返回具有最低優先級值的元素。您可以認為這個新集合類似于Queue<T>但每個入隊元素都有一個影響出隊行為的優先級值。
以下示例演示了.PriorityQueue<string, int>
// creates a priority queue of strings with integer priorities
var pq=new PriorityQueue<string, int>();
// enqueue elements with associated priorities
pq.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)并刪除了所有結構復制操作。
歸功于本亞當斯。
添加了僅限日期和時間的結構,具有以下特征:
這種改進具有以下好處:
在所有平臺上支持 Windows 和 IANA 時區
這種改進具有以下好處:
改進的時區顯示名稱
Unix 上的時區顯示名稱已得到改進:
還進行了以下附加改進:
改進了對 Windows ACL 的支持
System.Threading.AccessControl現在包括對與Windows 訪問控制列表(ACL) 交互的改進支持。新的重載被添加到Mutex和Semaphore的OpenExisting和TryOpenExisting方法EventWaitHandle中。這些具有“安全權限”實例的重載允許打開使用特殊Windows 安全屬性創建的線程同步對象的現有實例。
此更新與.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 mutex
Mutex 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 semaphore
Semaphore 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 handle
EventWaitHandle 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類現在具有允許一次性計算HMAC而無需分配的靜態方法。這些添加類似于在先前版本中添加的用于哈希生成的一次性方法。
該DependentHandle類型現在是公共的,具有以下 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();
}
}
它可用于創建高級系統,例如復雜的緩存系統或ConditionalWeakTable<TKey, TValue>類型?的自定義版本。例如,它將被MVVM Toolkit中的WeakReferenceMessenger類型使用,以避免在廣播消息時分配內存。
可移植線程池
.NET 線程池已作為托管實現重新實現,現在用作.NET 6 中的默認線程池。我們進行此更改以使所有.NET 應用程序都可以訪問同一個線程池,而不管是否正在使用CoreCLR、Mono 或任何其他運行時。作為此更改的一部分,我們沒有觀察到或預期任何功能或性能影響。
該團隊在此版本中對.NET JIT 編譯器進行了許多改進,在每個預覽帖子中都有記錄。這些更改中的大多數都提高了性能。這里介紹了一些RyuJIT 的亮點。
動態 PGO
在.NET 6 中,我們啟用了兩種形式的PGO(配置文件引導優化):
動態PGO 已經在文章前面的性能部分中介紹過。我將提供一個重新上限。
動態PGO 使JIT 能夠在運行時收集有關實際用于特定應用程序運行的代碼路徑和類型的信息。然后,JIT 可以根據這些代碼路徑優化代碼,有時會顯著提高性能。我們在測試和生產中都看到了兩位數的健康改進。有一組經典的編譯器技術在沒有PGO 的情況下使用JIT 或提前編譯都無法實現。我們現在能夠應用這些技術。熱/冷分離是一種這樣的技術,而去虛擬化是另一種技術。
要啟用動態PGO,請在應用程序將運行的環境中進行設置DOTNET\_TieredPGO=1。
如性能部分所述,動態PGO 將TechEmpower JSON"MVC"套件每秒的請求數提高了26%(510K -> 640K)。這是一個驚人的改進,無需更改代碼。
我們的目標是在未來的.NET 版本中默認啟用動態PGO,希望在.NET 7 中啟用。我們強烈建議您在應用程序中嘗試動態PGO 并向我們提供反饋。
完整的 PGO
要充分利用Dynamic PGO,您可以設置兩個額外的環境變量:DOTNET\_TC\_QuickJitForLoops=1和DOTNET\_ReadyToRun=0。 這確保了盡可能多的方法參與分層編譯。我們將此變體稱為 Full PGO 。與動態PGO 相比,完整PGO 可以提供更大的穩態性能優勢,但啟動時間會更慢(因為必須在第0 層運行更多方法)。
您不希望將此選項用于短期運行的無服務器應用程序,但對于長期運行的應用程序可能有意義。
在未來的版本中,我們計劃精簡和簡化這些選項,以便您可以更簡單地獲得完整PGO 的好處并用于更廣泛的應用程序。
靜態 PGO
我們目前使用 靜態 PGO 來優化.NET 庫程序集,例如R2R(Ready To Run)附帶的程序集System.Private.CoreLib。
靜態PGO 的好處是,在使用crossgen 將程序集編譯為R2R 格式時會進行優化。這意味著有運行時的好處而沒有運行時成本。這是非常重要的,也是PGO 對C++ 很重要的原因,例如。
循環對齊
內存對齊是現代計算中各種操作的共同要求。在.NET 5 中,我們開始在 32 字節邊界對齊方法。在.NET 6 中,我們添加了一項執行自適應循環對齊的功能,該功能在具有循環的方法中添加NOP填充指令,以便循環代碼從mod(16) 或mod(32) 內存地址開始。這些更改改進并穩定了.NET 代碼的性能。
在下面的冒泡排序圖中,數據點1 表示我們開始在32 字節邊界對齊方法的點。數據點2 表示我們也開始對齊內部循環的點。如您所見,基準測試的性能和穩定性都有很大提高。
硬件加速結構
結構是CLR 類型系統的重要組成部分。近年來,它們經常被用作整個.NET 庫中的性能原語。最近的例子ValueTask是ValueTuple和Span<T>。記錄結構是一個新的例子。在.NET 5 和.NET 6 中,我們一直在提高結構的性能,部分原因是通過確保結構是局部變量、參數或方法的返回值時可以保存在超快速CPU 寄存器中)。這對于使用向量計算的API 特別有用。
穩定性能測量
團隊中有大量從未出現在博客上的工程系統工作。這對于您使用的任何硬件或軟件產品都是如此。JIT 團隊開展了一個項目來穩定性能測量,目標是增加我們內部性能實驗室自動化自動報告的回歸值。這個項目很有趣,因為需要進行深入調查和產品更改才能實現穩定性。它還展示了我們為保持和提高績效而衡量的規模。
此圖像演示了不穩定的性能測量,其中性能在連續運行中在慢速和快速之間波動。x 軸是測試日期,y 軸是測試時間,以納秒為單位。到圖表末尾(提交這些更改后),您可以看到測量值穩定,結果最好。這張圖片展示了一個單一的測試。還有更多測試在dotnet/runtime #43227中被證明具有類似的行為。
Crossgen2 是crossgen 工具的替代品。它旨在滿足兩個結果:
這種轉換有點類似于本機代碼csc.exe 到托管代碼Roslyn 編譯器。Crossgen2 是用C# 編寫的,但是它沒有像Roslyn 那樣公開一個花哨的API。
我們可能已經/已經為.NET 6 和7 計劃了六個項目,這些項目依賴于crossgen2。矢量指令默認提議是我們希望為.NET 6 但更可能是.NET 7 進行的crossgen2 功能和產品更改的一個很好的例子。版本氣泡是另一個很好的例子。
Crossgen2 支持跨操作系統和架構維度的交叉編譯(因此稱為"crossgen")。這意味著您將能夠使用單個構建機器為所有目標生成本機代碼,至少與準備運行的代碼相關。但是,運行和測試該代碼是另一回事,為此您需要合適的硬件和操作系統。
第一步是用crossgen2編譯平臺本身。我們使用.NET 6 完成了所有架構的任務。因此,我們能夠在此版本中淘汰舊的crossgen。請注意,crossgen2 僅適用于CoreCLR,而不適用于基于Mono 的應用程序(它們具有一組單獨的代碼生成工具)。
這個項目——至少一開始——并不以性能為導向。目標是啟用更好的架構來托管RyuJIT(或任何其他)編譯器以離線方式生成代碼(不需要或啟動運行時)。
你可能會說“嘿……如果是用C# 編寫的,難道你不需要啟動運行時來運行crossgen2 嗎?” 是的,但這不是本文中“離線”的含義。當crossgen2 運行時,我們不使用運行crossgen2 的運行時附帶的JIT 來生成準備運行(R2R) 代碼. 那是行不通的,至少對于我們的目標來說是行不通的。想象一下crossgen2 在x64 機器上運行,我們需要為Arm64 生成代碼。Crossgen2 將Arm64 RyuJIT(針對x64 編譯)加載為原生插件,然后使用它生成Arm64 R2R 代碼。機器指令只是保存到文件中的字節流。它也可以在相反的方向工作。在Arm64 上,crossgen2 可以使用編譯為Arm64 的x64 RyuJIT 生成x64 代碼。我們使用相同的方法來針對x64 機器上的x64 代碼。Crossgen2 會加載一個RyuJIT,它是為任何需要的配置而構建的。這可能看起來很復雜,但如果您想啟用無縫的交叉定位模型,它就是您需要的那種系統,而這正是我們想要的。
我們希望只在一個版本中使用術語“crossgen2”,之后它將替換現有的crossgen,然后我們將回到使用術語“crossgen”來表示“crossgen2”。
EventPipe 是我們用于在進程內或進程外輸出事件、性能數據和計數器的跨平臺機制。從.NET 6 開始,我們已將實現從C++ 移至C。通過此更改,Mono 也使用EventPipe。這意味著CoreCLR 和Mono 都使用相同的事件基礎設施,包括.NET 診斷CLI 工具。
這一變化還伴隨著CoreCLR 的小幅減小:
庫 | 大小之后 - 大小之前 | 差異 |
libcoreclr.so | 7037856 – 7049408 | -11552 |
我們還進行了一些更改,以提高 EventPipe 在負載下的吞吐量。在最初的幾個預覽版中,我們進行了一系列更改,從而使吞吐量提高了.NET 5 的2.06 倍:
對于這個基準,越高越好。.NET 6 是橙色線,.NET 5 是藍色線。
對.NET SDK 進行了以下改進。
.NET 6 SDK 可選工作負載的 CLI 安裝
.NET 6 引入了SDK 工作負載的概念。工作負載是可選組件,可以安裝在.NET SDK 之上以啟用各種場景。.NET 6 中的新工作負載是:.NET MAUI 和Blazor WebAssembly AOT 工作負載。我們可能會在.NET 7 中創建新的工作負載(可能來自現有的SDK)。工作負載的最大好處是減少大小和可選性。我們希望隨著時間的推移使SDK 變得更小,并且只安裝您需要的組件。這個模型對開發者機器有好處,對CI 來說甚至更好。
Visual Studio 用戶并不真正需要擔心工作負載。工作負載功能經過專門設計,以便像Visual Studio 這樣的安裝協調器可以為您安裝工作負載。可以通過CLI 直接管理工作負載。
工作負載功能公開了用于管理工作負載的多個動詞,包括以下幾個:
update動詞查詢更新nuget.org的工作負載清單、更新本地清單、下載已安裝工作負載的新版本,然后刪除所有舊版本的工作負載。這類似于apt update && apt upgrade -y(用于基于Debian 的Linux 發行版)。將工作負載視為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
您現在可以在http://NuGet.org 中搜索帶有.dotnet new --search
模板安裝的其他改進包括支持切換以支持私有NuGet 源的授權憑據。--interactive
安裝CLI 模板后,您可以通過和檢查更新是否可用。--update-check--update-apply
NuGet 包驗證
包驗證工具使NuGet 庫開發人員能夠驗證他們的包是否一致且格式正確。
這包括:
該工具是SDK 的一部分。使用它的最簡單方法是在項目文件中設置一個新屬性。
<EnablePackageValidation> true </EnablePackageValidation>
更多 Roslyn 分析儀
在.NET 5 中,我們提供了大約250 個帶有.NET SDK 的分析器。其中許多已經存在,但作為NuGet 包在帶外發送。我們為 .NET 6 添加了更多分析器。
默認情況下,大多數新分析器都在信息級別啟用。您可以通過如下配置分析模式在警告級別啟用這些分析器:<AnalysisMode>All</AnalysisMode>
我們為.NET 6 發布了我們想要的一組分析器(加上一些附加功能),然后將它們中的大多數做成了可供抓取的。社區添加了幾個實現,包括這些。
感謝Meik Tranel和Newell Clark。
為 Platform Compatibility Analyzer 啟用自定義防護
CA1416 平臺兼容性分析器已經使用OperatingSystem和RuntimeInformation中的方法識別平臺防護,例如OperatingSystem.IsWindows和OperatingSystem.IsWindowsVersionAtLeast。但是,分析器無法識別任何其他保護可能性,例如緩存在字段或屬性中的平臺檢查結果,或者在輔助方法中定義了復雜的平臺檢查邏輯。
為了允許自定義守衛的可能性,我們添加了新屬性 SupportedOSPlatformGuard并UnsupportedOSPlatformGuard使用相應的平臺名稱和/或版本注釋自??定義守衛成員。此注釋被平臺兼容性分析器的流分析邏輯識別和尊重。
用法
[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 版本,在性能、功能、可用性和安全性方面都有很多的改進。我們希望您能找到許多改進,最終使您在日常開發中更有效率和能力,并提高性能或降低生產中應用程序的成本。我們已經開始從那些已經開始使用.NET 6 的人那里聽到好消息。
在Microsoft,我們還處于.NET 6 部署的早期階段,一些關鍵應用程序已經投入生產,未來幾周和幾個月內還會有更多應用程序推出。
.NET 6 是我們最新的LTS 版本。我們鼓勵每個人都轉向它,特別是如果您使用的是.NET 5。我們期待它成為有史以來采用速度最快的.NET 版本。
此版本是至少1000 人(但可能更多)的結果。這包括來自Microsoft 的.NET 團隊以及社區中的更多人。我試圖在這篇文章中包含許多社區貢獻的功能。感謝您抽出寶貴時間創建這些內容并完成我們的流程。我希望這次經歷是一次美好的經歷,并且更多的人會做出貢獻。
這篇文章是許多有才華的人合作的結果。貢獻包括團隊在整個發布過程中提供的功能內容、為此最終帖子創建的重要新內容,以及使最終內容達到您應得的質量所需的大量技術和散文更正。很高興為您制作它和所有其他帖子。
感謝您成為.NET 開發人員。
=====================================================================
本文關鍵字
WINDOWS 7 WIN7 WINDOWS 10 WIN10 暗黑破壞神2:重制版 測試版 暗黑2 教程 手柄支持 暗黑2重制版資源與破解 中文補丁 測試圖 資源鏈接
=====================================================================
話說自從重制版出來后我一直在關注,無奈年齡大了看孩子了實在沒有那么多時間了...
真是懷念年輕的時候啊~
然后因為重制版還沒有A測的時候,就早已經說明了游戲只能用WIN10跑.
個人實在不喜歡WIN10所以也就一直沒動手折騰.
最近發現有國外的補丁能讓暗黑破壞神2:重制版的A測客戶端可以運行在WIN7下,抽了一天空試了試,發現竟然能完美運行,并且發現這次的重制版游戲竟然能完美的支持手柄,真是爽爆了!
OK閑話不多說,進入正題.
首先,在下載游戲以及折騰這些補丁之前,請把自己電腦的所有殺毒軟件退出,包括WIN10自帶的.把UCA降到最低.這是個老生常談的問題.
1.先去下載游戲的A測客戶端文件并且要安裝好.
迅雷:magnet:?xt=urn:btih:6BB8E7F213E9653518C058B47DADCC2AC30CB294
百度網盤: https://pan.baidu.com/s/1VC4FnaXIYRcMFbMGkhrK2A
天翼云盤:http://lygzql.ys168.com/
2.下載的文件是壓縮包分卷,直接用WINRAR隨便找一個文件右鍵解壓出來,然后進入文件夾找到dotnet-sdk-3.1.408-win-x64.exe這個文件,打開,安裝.都是一些WINDOWS的組件.直接安裝就行了.
3.安裝后把離線中文補丁解壓縮后得到D2ROffline.exe與patches.txt兩個文件,復制放到游戲目錄與GAME.exe文件同級.截止到本文發表之前,目前的最新版本是2.06,已經支持中文并且可以玩所有職業.目前更新非常頻繁,感謝制作者.
4.WIN10可以嘗試準備開始游戲了.
5.如果你像我一樣是WIN7,還需要一個國外出的叫DX12 On 7的文件,文件名稱microsoft.direct3d.d3d12on7.1.1.0 .nupkg,下載后就一個文件,擴展名還是個非常不常見的,不知道怎么打開?
其實這就是一個壓縮文件,你可以強行用WINRAR或者WINZIP打開,或者把擴展名改成.ZIP后用解壓縮軟件打開.
然后依次進入microsoft.direct3d.d3d12on7.1.1.0\bin\x64,打開X64文件夾,里面有3個DLL文件.復制這個3個DLL文件放到游戲目錄,與GAME.exe文件同級.
這個文件,要求系統要最低安裝.NET Framework v4.7.2,如果你不確定自己裝沒裝,去添加刪除程序里面找,或者自己去微軟官方網站下載一個最新的重新安裝一下.
6.現在,WIN7系統的也可以開始游戲試試了.
在開始游戲之前,請確保你把能退出的程序全部退出了,電腦干干凈凈.
WIN7系統的,如果你是個老手,你的電腦應該保持的比較干凈,該裝的系統文件庫什么都全了.
其實無論什么時候,也應該保持自己的電腦不亂運行程序.
開始游戲請打開游戲目錄的D2ROffline.exe,這個文件.別的文件請不要動.
如果順利的話,你應該會順利的進入游戲.
如果碰到各種問題,
自己來下面對應自己的情況,看看能不能解決,能解決最好,如果不能,請你知道目前暗黑2重制版并不是正式版,只是個非常初級的測試版,出現問題其實很正常.
首次運行游戲,個別電腦可能會出現卡頓黑屏等問題,關閉游戲或者重啟電腦后,下次進游戲即可流程運行
有好多人反應中文離線補丁失效,進入游戲后提示無法進入單人游戲或者局域網游戲,這個問題只能說10%電腦會出現,
畢竟是離線補丁文件,并不是未加密游戲。大家可以參考一下本版的各類技術貼,如有新的離線補丁更新會弟一時間更新
如果出現以下癥狀(能進入游戲,但是無法創建角色單機游戲或者局域網游戲)
未經處理的異常: System.ComponentModel.Win32Exception: 僅完成部分的 ReadProcessMemory 或 WriteProcessMemory 請求。
在 System.Diagnostics.NtProcessManager.GetModuleInfos(Int32 processId, Boolean firstModuleOnly)
在 System.Diagnostics.NtProcessManager.GetFirstModuleInfo(Int32 processId)
在 System.Diagnostics.Process.get_MainModule()
在 D2RModding.Program.Main(String[] args)
說明離線補丁沒有完成
請在剛剛進入游戲的時候就立刻按住ALT+F4關閉游戲,然后立刻重新啟動游戲。
WIN7經過我說的辦法進入游戲后,不要動圖像選項,會跳出,如果必須要動,把游戲變成窗口化,會減少跳出的可能性.
7,說一下我的配置,很主流的配置,I5 6600+16G+1060 6G,WIN7x64專業版.
游戲把抗鋸齒跟垂直同步關閉后,非常流暢.
8,到這,基本上游戲的問題說完了.
然后說一說游戲對手柄支持的情況.
我個人從06年左右就開始在電腦上用手柄玩游戲,因為我發現好多游戲真的其實開發的時候就是優先手柄操作而開發的,特別是我又特別喜歡動作游戲,可能這個毛病跟從小家里一直有游戲機有關系,后來入了XBOX360后發現XBOX360的手柄實在是太舒服了,毫不猶豫的買了一套PC無線套件來給PC用.然后就發現了新天地,絕大部分單機游戲都能無縫支持微軟的XBOX360手柄.不用驅動,不用調整什么,進游戲就能用,而且舒服的很.
本來把暗黑2重制版在WIN7上跑起來我已經非常滿足了,可是還是用手柄試了一下,這一試可真的把我驚到了,游戲對手柄支持非常好!及其的好!
而且手柄玩比用鼠標鍵盤玩更容易操作!
下面發一些圖,然后隨便說說.
進入游戲后,手柄打開的情況下,點A鍵,游戲會卡一下然后自動切換成手柄的界面.你們看看,跟用鍵鼠不一樣了吧.
手柄的官方操作說明
儲存箱跟共享儲存箱都可以.
技能樹
傭兵裝備
裝備對比按Y
包包很亂?隨便在一個物品上按住L3,會自動整理包裹!
手柄最舒服的地方在于,在野外的時候,當你走到能互動的東西旁邊時,會自動顯示石頭,門之類的.
然后就是選怪跟走路都只靠左搖桿就行了,指向性很舒服,系統會自動幫你標記到你想打的怪.
然后碰到暗金怪,他們也有不一樣的標識.
撿東西的時候,當你腳下或者旁邊很近的地方有可以拾取的東西時,會自動顯示,然后2秒左右自動取消,撿東西跟標記怪物一樣,也是用左搖桿,會自動標識到物品,按A撿,人物不會走過去.按住LB會一直顯示所有地上的東西.
目前我個人非常滿足
我的配置目前真的不算高了,但是玩起來非常流暢.只關閉了抗鋸齒跟垂直同步.因為我是WIN7,發現抗鋸齒有很小的幾率跳出所以關閉了.
幀數的話,我試了2個測試軟件,一個Fraps,一個微星的MSI Afterburner,都不能在游戲里面顯示幀數,很奇怪.雖然測不到幀數,但是我玩單機游戲這么多年,基本上能判斷個差不多.
我那個配置大家也都看到了,我用亞馬遜,別管怪多怪少,基本上個人感覺是足60幀的,很流暢很絲滑,只是有的時候畫面過渡的時候有一些輕微的能感覺到的不流暢,個人判斷應該是游戲的事,或者是WIN7補丁的事,畢竟游戲只是A測,還有很長的路要走.而且我沒有把游戲放到固態硬盤.過幾天我要做的就是把游戲在WIN10的電腦上跑一下,然后放到固態硬盤里面再試試.
個人的幾點期待
希望暴雪把暗黑2重制版還是做成純單機,就像老暗黑那樣.不聯網也能啟動.
希望暴雪再完善一點游戲機制,目前字體方面我看了下,覺得不太清楚.
人物跑動起來,覺得沒有老版那樣靈動,有點飄,有點溜冰的感覺,這個詞我記得當年總是用來說FIFA足球,哈哈.
但是無論如何,暗黑2重制了,而且目前看來比較成功.
希望各種民間補丁跟大箱子都趕緊來,也希望暴雪不要堵死全部的口子,讓大家玩爽.
最后,我會去補票的.