99er久久国产精品先锋_亚洲丰满少妇撒尿BBo_老外和中国女人毛片免费视频_思思热在线视频网站_av无码不卡高清_国产 激情 自拍_激情综合色婷婷激情丁香_少妇与子乱A级全毛片_男人捅女人的软件_日本欧美日韩

...

宣布 .NET 6 — 迄今為止最快的 .NET

2021-12-13


2021 年 11 月 8 日 (以下是譯文)


歡迎使用 .NET 6。今天的發(fā)布是 .NET 團(tuán)隊(duì)和社區(qū)一年多來努力的結(jié)果。C# 10 和 F# 6 提供了語言改進(jìn),使您的代碼更簡單、更好。性能有了巨大的提升,我們已經(jīng)看到降低了 Microsoft 托管云服務(wù)的成本。.NET 6 是第一個(gè)原生支持 Apple Silicon (Arm64) 的版本,并且還針對(duì) Windows Arm64 進(jìn)行了改進(jìn)。我們構(gòu)建了一個(gè)新的動(dòng)態(tài)配置文件引導(dǎo)優(yōu)化 (PGO) 系統(tǒng),該系統(tǒng)可提供僅在運(yùn)行時(shí)才可能進(jìn)行的深度優(yōu)化。使用dotnet monitorOpenTelemetry改進(jìn)了云診斷。WebAssembly支持更強(qiáng)大、更高效。添加了新的 API,用于HTTP/3,處理 JSON數(shù)學(xué),直接操作內(nèi)存。.NET 6 將得到三年支持。開發(fā)人員已經(jīng)開始將應(yīng)用程序升級(jí)到 .NET 6,我們已經(jīng)在生產(chǎn)中聽到了很好的早期結(jié)果。.NET 6 已為您的應(yīng)用做好準(zhǔn)備。

您可以下載適用于 Linux、macOS 和 Windows 的.NET 6

有關(guān)Web 方案的新增功能,請(qǐng)參閱ASP.NET Core帖子。

Visual Studio 2022 也在今天發(fā)布。閱讀公告觀看發(fā)布活動(dòng)以了解有關(guān)發(fā)布的更多信息。

PowerShell 7.2也在今天發(fā)布,基于 .NET 6。PowerShell 用戶可以訪問與 .NET 開發(fā)人員相同的性能改進(jìn)和 API。

.NET Conf是一個(gè)為期三天的免費(fèi)虛擬開發(fā)人員活動(dòng),旨在慶祝 .NET 的主要版本。它將于明天開始,并于 11 月 9 日至 11 日舉行,屆時(shí)將有來自我們團(tuán)隊(duì)、Microsoft 團(tuán)隊(duì)和更廣泛社區(qū)的演講者參加 80 多場會(huì)議。收聽學(xué)習(xí)并與我們互動(dòng)。

查看新的對(duì)話帖子,就最新的 .NET 功能進(jìn)行工程師對(duì)工程師的深入討論。

.NET 6 亮點(diǎn)

.NET 6 是:

該版本包括大約一萬個(gè) git 提交。即使這篇文章很長,它也跳過了許多改進(jìn)。您必須下載并試用 .NET 6 才能看到所有新內(nèi)容。

支持

.NET 6 是一個(gè)長期支持 (LTS) 版本,將支持三年。它支持多種操作系統(tǒng),包括 macOS Apple Silicon 和 Windows Arm64。

紅帽與 .NET 團(tuán)隊(duì)合作紅帽企業(yè) Linux 上支持 .NET。在 RHEL 8 及更高版本上,.NET 6 將可用于 AMD 和 Intel (x64_64)、ARM (aarch64) 以及 IBM Z 和 LinuxONE (s390x) 架構(gòu)。

請(qǐng)開始將您的應(yīng)用程序遷移到 .NET 6,尤其是 .NET 5 應(yīng)用程序。我們從早期采用者那里聽說,從 .NET Core 3.1 和 .NET 5 升級(jí)到 .NET 6 很簡單。

Visual Studio 2022Visual Studio 2022 for Mac支持 .NET 6 。Visual Studio 2019、Visual Studio for Mac 8 或 MSBuild 16 不支持它。如果要使用 .NET 6,則需要升級(jí)到Visual Studio 2022(現(xiàn)在也是 64 位)。Visual Studio CodeC# 擴(kuò)展支持 .NET 6 。

Azure 應(yīng)用服務(wù):

注意:如果您的應(yīng)用已在應(yīng)用服務(wù)上運(yùn)行 .NET 6 預(yù)覽版或 RC 構(gòu)建,則一旦 .NET 6 運(yùn)行時(shí)和 SDK 部署到您所在的區(qū)域,它將在第一次重新啟動(dòng)時(shí)自動(dòng)更新。如果您部署了自包含應(yīng)用程序,則需要重新構(gòu)建和重新部署。

統(tǒng)一擴(kuò)展平臺(tái)

.NET 6 為瀏覽器、桌面、IoT移動(dòng)應(yīng)用程序提供了一個(gè)統(tǒng)一的平臺(tái)。底層平臺(tái)已更新,以滿足所有應(yīng)用程序類型的需求,并使您可以輕松地在所有應(yīng)用程序中重用代碼。新功能和改進(jìn)可同時(shí)用于所有應(yīng)用程序,因此您在云中或移動(dòng)設(shè)備上運(yùn)行的代碼具有相同的行為方式并具有相同的優(yōu)勢。





隨著每個(gè)版本的發(fā)布,.NET 開發(fā)人員的影響范圍不斷擴(kuò)大。機(jī)器學(xué)習(xí)WebAssembly是最近添加的兩個(gè)。例如,通過機(jī)器學(xué)習(xí),您可以編寫應(yīng)用程序來查找流數(shù)據(jù)中的異常情況。使用 WebAssembly,您可以在瀏覽器中托管 .NET 應(yīng)用程序,就像 HTML 和 JavaScript 一樣,或者將它們與 HTML 和 JavaScript 混合使用。

最令人興奮的新增功能之一是.NET 多平臺(tái)應(yīng)用程序 UI (.NET MAUI)。您現(xiàn)在可以在單個(gè)項(xiàng)目中編寫代碼,從而提供跨桌面和移動(dòng)操作系統(tǒng)的現(xiàn)代客戶端應(yīng)用程序體驗(yàn)。.NET MAUI 的發(fā)布時(shí)間將比 .NET 6 晚一點(diǎn)。我們?cè)?.NET MAUI 上投入了大量時(shí)間和精力,很高興能夠發(fā)布它并看到 .NET MAUI 應(yīng)用程序投入生產(chǎn)。

當(dāng)然,.NET 應(yīng)用程序也可以在Windows 桌面上使用(使用Windows 窗體WPF)以及在云中使用http://ASP.NETCore。它們是我們提供時(shí)間最長的應(yīng)用程序類型,并且仍然非常受歡迎,我們?cè)?.NET 6 中對(duì)它們進(jìn)行了改進(jìn)。

面向 .NET 6

繼續(xù)以廣泛的平臺(tái)為主題,在所有這些操作系統(tǒng)上編寫 .NET 代碼很容易。

以 .NET 6目標(biāo),您需要使用 .NET 6 目標(biāo)框架,如下所示:

net6.0

該目標(biāo)框架名字對(duì)象(TFM),您可以訪問所有的跨平臺(tái)的API,.NET提供。如果您正在編寫控制臺(tái)應(yīng)用程序、http://ASP.NETCore 應(yīng)用程序或可重用的跨平臺(tái)庫,這是最佳選擇。net6.0

如果您的目標(biāo)是特定的操作系統(tǒng)(例如編寫Windows 窗體或 iOS 應(yīng)用程序),那么還有另一組 TFM(每個(gè)都針對(duì)一個(gè)不言而喻的操作系統(tǒng))供您使用。它們使您可以訪問所有 API以及一系列特定于操作系統(tǒng)的 API 。net6.0

  • net6.0-android

  • net6.0-ios

  • net6.0-maccatalyst

  • net6.0-tvos

  • net6.0-windows

每個(gè)無版本的 TFM 都相當(dāng)于針對(duì) .NET 6 支持的最低操作系統(tǒng)版本。如果您想要特定或訪問更新的 API,可以指定操作系統(tǒng)版本。

的和TFMS支持(同.NET 5)。Android 和 Apple TFM 是 .NET 6 的新增功能,目前處于預(yù)覽階段。稍后的 .NET 6 更新將支持它們。net6.0net6.0-windows

操作系統(tǒng)特定的 TFM 之間沒有兼容性關(guān)系。例如,與. 如果您想共享代碼,您需要使用帶有語句的源代碼或帶有目標(biāo)代碼的二進(jìn)制文件來實(shí)現(xiàn)。net6.0-iosnet6.0-tvos#ifnet6.0

表現(xiàn)

自從我們啟動(dòng) .NET Core 項(xiàng)目以來,該團(tuán)隊(duì)就一直非常關(guān)注性能。Stephen Toub在捕捉每個(gè)版本的 .NET 性能進(jìn)展方面做得非常出色。如果您還沒有機(jī)會(huì),我建議您查看他在 .NET 6 中的性能改進(jìn)帖子。

在這篇博文中,我收集了一些您想了解的重大性能改進(jìn),包括文件 IO、界面轉(zhuǎn)換、PGO 和 System.Text.Json。

動(dòng)態(tài) PGO

動(dòng)態(tài)輪廓引導(dǎo)優(yōu)化 (PGO)可以顯著提高穩(wěn)態(tài)性能。例如,PGO 使 TechEmpower JSON“MVC”套件的每秒請(qǐng)求數(shù)提高了 26%(510K -> 640K)。

動(dòng)態(tài) PGO 建立在分層編譯之上,它使方法能夠首先非??焖俚鼐幾g(稱為“第 0 層”)以提高啟動(dòng)性能,然后在啟用大量優(yōu)化的情況下隨后重新編譯(稱為“第 1 層”)一旦這種方法被證明是有效的。該模型使方法能夠在第 0 層中進(jìn)行檢測,以允許對(duì)代碼的執(zhí)行進(jìn)行各種觀察。當(dāng)這些方法在第 1 層重新編譯時(shí),從第 0 層執(zhí)行中收集的信息將用于更好地優(yōu)化第 1 層代碼。這就是機(jī)制的本質(zhì)。

動(dòng)態(tài) PGO 的啟動(dòng)時(shí)間將比默認(rèn)運(yùn)行時(shí)稍慢,因?yàn)樵诘?0 層方法中運(yùn)行額外的代碼來觀察方法行為。

要啟用動(dòng)態(tài) PGO,請(qǐng)?jiān)谀膽?yīng)用程序?qū)⑦\(yùn)行的環(huán)境中進(jìn)行設(shè)置。您還必須確保啟用分層編譯(默認(rèn)情況下)。動(dòng)態(tài) PGO 是可選的,因?yàn)樗且环N新的、有影響力的技術(shù)。我們希望發(fā)布選擇性使用和相關(guān)反饋,以確保它經(jīng)過全面壓力測試。我們對(duì)分層編譯做了同樣的事情。至少一個(gè)非常大的 Microsoft 服務(wù)支持動(dòng)態(tài) PGO,并且已經(jīng)在生產(chǎn)中使用它。我們鼓勵(lì)您嘗試一下。DOTNET_TieredPGO=1

您可以在 .NET 6中的性能博文中看到更多關(guān)于動(dòng)態(tài) PGO 優(yōu)勢的信息,包括以下微基準(zhǔn)測試,它測量特定 LINQ 枚舉器的成本。

private IEnumerator _source = Enumerable.Range(0, long.MaxValue).GetEnumerator();

[Benchmark]
public void MoveNext() => _source.MoveNext();

這是有和沒有動(dòng)態(tài) PGO 的結(jié)果。




這是一個(gè)相當(dāng)大的差異,但也增加了代碼大小,這可能會(huì)讓一些讀者感到驚訝。這是 JIT 生成的匯編代碼的大小,而不是內(nèi)存分配(這是一個(gè)更常見的焦點(diǎn))。.NET 6 Performance 帖子對(duì)此有很好的解釋。

PGO 實(shí)現(xiàn)中常見的一種優(yōu)化是“熱/冷拆分”,其中經(jīng)常執(zhí)行的方法部分(“熱”)在方法開始時(shí)靠近在一起,而不經(jīng)常執(zhí)行的方法部分(“冷”)被移到一起移動(dòng)到方法的末尾。這可以更好地使用指令緩存并最大限度地減少可能未使用的代碼的負(fù)載。

作為上下文,接口調(diào)度是 .NET 中最昂貴的調(diào)用類型。非虛擬方法調(diào)用是最快的,甚至更快的是可以通過內(nèi)聯(lián)消除的調(diào)用。在這種情況下,動(dòng)態(tài) PGO 為MoveNext. 第一個(gè) — 熱的 — 是直接調(diào)用,另一個(gè) — 冷的 — 是通過. 如果最熱門的人大部分時(shí)間都被跟注,那將是一場巨大的勝利。Enumerable+RangeIterator.MoveNextIEnumerator

這就是魔法。當(dāng) JIT 檢測此方法的第 0 層代碼時(shí),包括檢測此接口分派以跟蹤_source每次調(diào)用的具體類型。并且 JIT 發(fā)現(xiàn)每次調(diào)用都在一個(gè)名為 的類型上,這是一個(gè)用于在實(shí)現(xiàn)內(nèi)部實(shí)現(xiàn)的私有類。因此,對(duì)于第 1 層,JIT 已發(fā)出檢查以查看類型是否為:如果不是,則它跳轉(zhuǎn)到我們之前強(qiáng)調(diào)的執(zhí)行正常接口調(diào)度的冷部分。但如果是——基于分析數(shù)據(jù)預(yù)計(jì)在絕大多數(shù)時(shí)間都是這種情況——然后它可以繼續(xù)直接調(diào)用Enumerable+RangeIteratorEnumerable.RangeEnumerable_sourceEnumerable+RangeIteratorEnumerable+RangeIterator.MoveNext方法,非虛擬化。不僅如此,它還認(rèn)為內(nèi)聯(lián)該MoveNext方法是有利可圖的。最終效果是生成的匯編代碼稍大一些,但針對(duì)預(yù)期最常見的確切場景進(jìn)行了優(yōu)化。當(dāng)我們開始構(gòu)建動(dòng)態(tài) PGO 時(shí),這些就是我們想要的勝利。

動(dòng)態(tài) PGO 在 RyuJIT 部分再次討論。

文件 IO 改進(jìn)

FileStream幾乎完全用 .NET 6 重寫,重點(diǎn)是提高異步文件 IO 性能。在 Windows 上,實(shí)現(xiàn)不再使用阻塞 API 并且可以快幾倍!我們還改進(jìn)了所有平臺(tái)上的內(nèi)存使用。在第一個(gè)異步操作(通常分配)之后,我們已經(jīng)使異步操作無分配!此外,當(dāng) Windows 和 Unix 實(shí)現(xiàn)不同(并且這是可能的)時(shí),我們使邊緣情況的行為變得統(tǒng)一。

這種重寫的性能改進(jìn)使所有操作系統(tǒng)受益。Windows 的好處是最高的,因?yàn)樗h(yuǎn)遠(yuǎn)落后。macOS 和 Linux 用戶還應(yīng)該看到顯著的FileStream性能改進(jìn)。

以下基準(zhǔn)測試將 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 驅(qū)動(dòng)器的 Windows 上,我們觀察到了4 倍的加速和超過1200 倍的分配下降:

我們還認(rèn)識(shí)到需要更多高性能文件 IO 功能:并發(fā)讀寫和分散/聚集 IO。針對(duì)這些情況,我們?yōu)楹皖愐肓诵碌?API 。System.IO.FileSystem.IO.RandomAccess

async Task AllOrNothingAsync(string path, IReadOnlyList 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!
}

示例演示:

預(yù)分配大小功能提高了性能,因?yàn)閷懭氩僮鞑恍枰獢U(kuò)展文件,而且文件碎片化的可能性較小。這種方法提高了可靠性,因?yàn)閷懖僮鲗⒉辉僖蚩臻g不足而失敗,因?yàn)榭臻g已經(jīng)被保留。Scatter/Gather IO API 減少了寫入數(shù)據(jù)所需的系統(tǒng)調(diào)用次數(shù)。

更快的界面檢查和轉(zhuǎn)換

界面投射性能提升了 16% – 38%。這種改進(jìn)對(duì)于 C# 與接口之間的模式匹配特別有用。





該圖表展示了代表性基準(zhǔn)的改進(jìn)規(guī)模。

將 .NET 運(yùn)行時(shí)的一部分從 C++ 遷移到托管 C# 的最大優(yōu)勢之一是它降低了貢獻(xiàn)的障礙。這包括接口轉(zhuǎn)換,它作為 .NET 6 的早期更改移至 C#。.NET 生態(tài)系統(tǒng)中通曉 C# 的人多于 C++(并且運(yùn)行時(shí)使用具有挑戰(zhàn)性的 C++ 模式)。能夠閱讀組成運(yùn)行時(shí)的一些代碼是培養(yǎng)對(duì)以各種形式貢獻(xiàn)的信心的重要一步。

歸功于本·亞當(dāng)斯。

System.Text.Json 源代碼生成器

我們為 System.Text.Json添加了一個(gè)源代碼生成器,它避免了在運(yùn)行時(shí)進(jìn)行反射和代碼生成的需要,并且可以在構(gòu)建時(shí)生成最佳序列化代碼。序列化程序通常使用非常保守的技術(shù)編寫,因?yàn)樗鼈儽仨毴绱恕5?,如果您閱讀自己的序列化源代碼(使用序列化程序),您會(huì)看到哪些明顯的選擇可以使序列化程序在您的特定情況下更加優(yōu)化。這正是這個(gè)新的源生成器所做的。

除了提高性能和減少內(nèi)存之外,源代碼生成器還可以生成最適合程序集修整的代碼。這有助于開發(fā)更小的應(yīng)用程序。

序列化POCO是一個(gè)非常常見的場景。使用新源發(fā)生器,我們觀察到,序列化是?1.6倍快與我們的基準(zhǔn)。

TechEmpower緩存基準(zhǔn)行使平臺(tái)或從數(shù)據(jù)庫來源的信息架構(gòu)的內(nèi)存緩存?;鶞?zhǔn)測試的 .NET 實(shí)現(xiàn)對(duì)緩存數(shù)據(jù)執(zhí)行 JSON 序列化,以便將其作為對(duì)測試工具的響應(yīng)發(fā)送。

我們觀察到~100K RPS 增益(~40% 增加)。與MemoryCache 性能改進(jìn)相結(jié)合時(shí),.NET 6 的吞吐量比 .NET 5 高 50% !

C# 10

歡迎使用 C# 10。C# 10 的一個(gè)主要主題是繼續(xù)從C# 9 中的頂級(jí)語句開始的簡化之旅。新功能從 中刪除了更多的儀式,導(dǎo)致程序短至一行。他們的靈感來自與沒有 C# 經(jīng)驗(yàn)的人(學(xué)生、專業(yè)開發(fā)人員和其他人)交談,并學(xué)習(xí)對(duì)他們來說最有效且直觀的方法。Program.cs

大多數(shù).NET SDK 模板已經(jīng)更新,以提供 C# 10 現(xiàn)在可以實(shí)現(xiàn)的更簡單、更簡潔的體驗(yàn)。我們聽到反饋說有些人不喜歡新模板,因?yàn)樗鼈儾皇菫閷<以O(shè)計(jì)的,刪除面向?qū)ο?,刪除在編寫 C# 的第一天就需要學(xué)習(xí)的重要概念,或鼓勵(lì)在一個(gè)文件中編寫整個(gè)程序??陀^地說,這些觀點(diǎn)都不是真的。新模型同樣適用于學(xué)生和專業(yè)開發(fā)人員。然而,它與我們?cè)?.NET 6 之前擁有的 C 派生模型不同。

C# 10 中還有其他一些功能和改進(jìn),包括記錄結(jié)構(gòu)。

全局 using 指令

全局 using 指令讓您using只需指定一次指令,并將其應(yīng)用于您編譯的每個(gè)文件。

以下示例顯示了語法的廣度:

  • global using System;

  • global using static System.Console;

  • global using Env = System.Environment;

您可以將語句放在任何文件中,包括在.global using.csProgram.cs

隱式 using 是一個(gè) MSBuild 概念,它根據(jù) SDK自動(dòng)添加一組指令。例如,控制臺(tái)應(yīng)用隱式使用不同于http://ASP.NETCore。global using

隱式使用是選擇加入的,并在以下位置啟用PropertyGroup:

  • enable

隱式使用是現(xiàn)有項(xiàng)目的選擇加入,但默認(rèn)情況下包含在新的 C# 項(xiàng)目中。有關(guān)更多信息,請(qǐng)參閱隱式使用。

文件范圍的命名空間

文件范圍的命名空間使您可以為整個(gè)文件聲明命名空間,而無需將其余內(nèi)容嵌套在. 只允許一個(gè),并且它必須出現(xiàn)在聲明任何類型之前。{ ... }

新語法是一行:

namespace MyNamespace;

class MyClass { ... } // Not indented

這種新語法是三行縮進(jìn)樣式的替代:

namespace MyNamespace
{
    class MyClass { ... } // Everything is indented
}

好處是在整個(gè)文件都在同一個(gè)命名空間中的極其常見的情況下減少縮進(jìn)。

記錄結(jié)構(gòu)

C# 9 引入了記錄作為類的一種特殊的面向值的形式。在 C# 10 中,您還可以聲明結(jié)構(gòu)記錄。C# 中的結(jié)構(gòu)已經(jīng)具有值相等性,但記錄結(jié)構(gòu)添加了一個(gè)==運(yùn)算符和一個(gè) 的實(shí)現(xiàn),以及一個(gè)基于值的實(shí)現(xiàn):IEquatableToString

public record struct Person
{
    public string FirstName { get; init; }
    public string LastName { get; init; }
}

就像記錄類一樣,記錄結(jié)構(gòu)可以是“位置”的,這意味著它們有一個(gè)主構(gòu)造函數(shù),它隱式聲明了與參數(shù)對(duì)應(yīng)的公共成員:

public record struct Person(string FirstName, string LastName);

但是,與記錄類不同,隱式公共成員是可變的自動(dòng)實(shí)現(xiàn)的屬性。這是因?yàn)橛涗浗Y(jié)構(gòu)是元組的自然成長故事。例如,如果您有一個(gè)返回類型,并且您想將其擴(kuò)展為命名類型,您可以輕松聲明相應(yīng)的位置結(jié)構(gòu)記錄并維護(hù)可變語義。(string FirstName, string LastName)

如果您想要一個(gè)具有只讀屬性的不可變記錄,您可以聲明整個(gè)記錄結(jié)構(gòu)readonly(就像其他結(jié)構(gòu)一樣):

public readonly record struct Person(string FirstName, string LastName);

C# 10 不僅支持with記錄結(jié)構(gòu)的表達(dá)式,還支持所有結(jié)構(gòu)以及匿名類型的表達(dá)式:

var updatedPerson = person with { FirstName = "Mary" };

F# 6

F# 6旨在讓 F# 更簡單、更高效。這適用于語言設(shè)計(jì)、庫和工具。我們對(duì) F# 6(及更高版本)的目標(biāo)是消除語言中讓用戶感到驚訝或?qū)W(xué)習(xí) F# 造成障礙的極端情況。我們很高興與 F# 社區(qū)合作進(jìn)行這項(xiàng)持續(xù)的工作。

使 F# 更快、更具互操作性

新語法直接創(chuàng)建一個(gè)任務(wù)并啟動(dòng)它。這是 F# 6 中最重要的特性之一,它使異步任務(wù)更簡單、性能更高,并且與 C# 和其他 .NET 語言的互操作性更強(qiáng)。以前,創(chuàng)建 .NET 任務(wù)需要使用創(chuàng)建任務(wù)和調(diào)用.task {…}async {…}Async.StartImmediateAsTask

該功能建立在稱為“可恢復(fù)代碼”RFC FS-1087的基礎(chǔ)之上。可恢復(fù)代碼是一個(gè)核心特性,我們希望在未來使用它來構(gòu)建其他高性能異步和屈服狀態(tài)機(jī)。task {…}

F# 6 還為庫作者添加了其他性能特性,包括InlineIfLambdaF# 活動(dòng)模式的未裝箱表示。一個(gè)特別顯著的性能改進(jìn)是在列表和數(shù)組表達(dá)式的編譯中,現(xiàn)在它們的速度提高了4 倍,并且調(diào)試也更好更簡單。

使 F# 更易于學(xué)習(xí)且更統(tǒng)一

F# 6 啟用索引語法。到目前為止,F(xiàn)# 一直使用 expr.[idx] 進(jìn)行索引。刪除點(diǎn)符號(hào)是基于第一次使用 F# 用戶的反復(fù)反饋,即使用點(diǎn)是與他們期望的標(biāo)準(zhǔn)實(shí)踐的不必要的分歧。在新代碼中,我們建議系統(tǒng)地使用新的索引語法。作為一個(gè)社區(qū),我們都應(yīng)該改用這種語法。expr[idx]expr[idx]

F# 社區(qū)做出了重要改進(jìn),使 F# 語言在 F# 6 中更加統(tǒng)一。其中最重要的是消除了 F# 縮進(jìn)規(guī)則中的許多不一致和限制。其他使 F# 更加統(tǒng)一的設(shè)計(jì)添加包括添加as模式;在計(jì)算表達(dá)式中允許“重載自定義操作”(對(duì) DSL 有用);允許_丟棄use綁定并允許輸出中的二進(jìn)制格式。F# 核心庫添加了用于對(duì)列表、數(shù)組和序列進(jìn)行復(fù)制和更新的新函數(shù),以及其他內(nèi)在函數(shù)。從 2.0 開始不推薦使用的 F# 的一些遺留功能現(xiàn)在會(huì)導(dǎo)致錯(cuò)誤。其中許多更改更好地使 F# 符合您的期望,從而減少了意外。%BNativePtr

F# 6 還添加了對(duì) F# 中其他“隱式”和“類型導(dǎo)向”轉(zhuǎn)換的支持。這意味著更少的顯式向上轉(zhuǎn)換,并增加了對(duì) .NET 樣式隱式轉(zhuǎn)換的一流支持。F# 還進(jìn)行了調(diào)整,以更好地適應(yīng)使用 64 位整數(shù)的數(shù)字庫時(shí)代,并對(duì) 32 位整數(shù)進(jìn)行隱式加寬。

改進(jìn) F# 工具

F# 6 中的工具改進(jìn)使日常編碼變得更容易。新的“管道調(diào)試”允許您單步執(zhí)行、設(shè)置斷點(diǎn)并檢查 F# 管道語法的中間值。陰影值的調(diào)試顯示已得到改進(jìn),消除了調(diào)試時(shí)常見的混淆源?,F(xiàn)在,F(xiàn)# 工具的性能也更高,F(xiàn)# 編譯器并行執(zhí)行解析階段。F# IDE 工具也得到了改進(jìn)。F# 腳本現(xiàn)在更加強(qiáng)大,允許您通過文件固定使用的 .NET SDK 版本。input |> f1 |> f2global.json

熱重載

熱重載是另一個(gè)性能特性,專注于開發(fā)人員的生產(chǎn)力。它使您能夠?qū)φ谶\(yùn)行的應(yīng)用程序進(jìn)行各種代碼編輯,從而減少您等待應(yīng)用程序重新構(gòu)建、重新啟動(dòng)或重新導(dǎo)航到進(jìn)行代碼更改后所在位置所需的時(shí)間。

熱重載可通過dotnet watchCLI 工具和 Visual Studio 2022 使用。您可以將熱重載用于多種應(yīng)用程序類型,例如 ASP.NET Core、Blazor、.NET MAUI、控制臺(tái)、Windows 窗體 (WinForms)、WPF、WinUI 3、Azure 函數(shù)等。

使用 CLI 時(shí),只需使用 啟動(dòng)您的 .NET 6 應(yīng)用程序dotnet watch,進(jìn)行任何支持的編輯,并在保存文件時(shí)(如在 Visual Studio Code 中)將立即應(yīng)用這些更改。如果不支持更改,詳細(xì)信息將記錄到命令窗口。





此圖像顯示正在啟動(dòng)的 MVC 應(yīng)用程序dotnet watch。我對(duì)和文件進(jìn)行了編輯(如日志中所報(bào)告的那樣),并且兩者都被應(yīng)用到代碼中并在不到半秒的時(shí)間內(nèi)很快地反映在瀏覽器中。.cs.cshtml

使用 Visual Studio 2022 時(shí),只需啟動(dòng)您的應(yīng)用程序,進(jìn)行受支持的更改,然后使用新的“熱重載”按鈕(如下圖所示)應(yīng)用這些更改。您還可以選擇通過同一按鈕上的下拉菜單在保存時(shí)應(yīng)用更改。使用 Visual Studio 2022 時(shí),Hot Reload 可用于多個(gè) .NET 版本、.NET 5+、.NET Core 和 .NET Framework。例如,您將能夠?qū)nClickEvent按鈕的處理程序進(jìn)行代碼隱藏更改。Main應(yīng)用程序的方法不支持它。





注意:RuntimeInformation.FrameworkDescription中存在一個(gè)錯(cuò)誤,錯(cuò)誤在該圖像中顯示,將很快修復(fù)。

熱重載還與現(xiàn)有的“編輯并繼續(xù)”功能(在斷點(diǎn)處停止時(shí))和 XAML 熱重載協(xié)同工作,用于實(shí)時(shí)編輯應(yīng)用程序 UI。目前支持 C# 和 Visual Basic 應(yīng)用程序(不是 F#)。

安全

.NET 6 中的安全性得到了顯著改善。它始終是團(tuán)隊(duì)的重要關(guān)注點(diǎn),包括威脅建模、加密和縱深防御緩解措施。

在 Linux 上,我們依賴OpenSSL進(jìn)行所有加密操作,包括 TLS(HTTPS 需要)。在 macOS 和 Windows 上,我們依賴操作系統(tǒng)提供的功能來實(shí)現(xiàn)相同的目的。對(duì)于 .NET 的每個(gè)新版本,我們經(jīng)常需要添加對(duì)新版本 OpenSSL 的支持。.NET 6 添加了對(duì)OpenSSL 3 的支持。

OpenSSL 3 的最大變化是改進(jìn)的FIPS 140-2模塊和更簡單的許可。

.NET 6 需要 OpenSSL 1.1 或更高版本,并且更喜歡它可以找到的最高安裝版本的 OpenSSL,直到并包括 v3。在一般情況下,當(dāng)您使用的 Linux 發(fā)行版切換為默認(rèn)設(shè)置時(shí),您最有可能開始使用 OpenSSL 3。大多數(shù)發(fā)行版還沒有這樣做。例如,如果您在 Red Hat 8 或 Ubuntu 20.04 上安裝 .NET 6,您將不會(huì)(在撰寫本文時(shí))開始使用 OpenSSL 3。

OpenSSL 3、Windows 10 21H1 和 Windows Server 2022 都支持ChaCha20Poly1305。您可以在 .NET 6 中使用這種新的經(jīng)過身份驗(yàn)證的加密方案(假設(shè)您的環(huán)境支持它)。

感謝凱文-瓊斯為ChaCha20Poly1305 Linux支持。

我們還發(fā)布了新的運(yùn)行時(shí)安全緩解路線圖。重要的是您使用的運(yùn)行時(shí)不受教科書式攻擊類型的影響。我們正在滿足這種需求。在 .NET 6 中,我們構(gòu)建了W^X英特爾控制流執(zhí)行技術(shù) (CET) 的初始實(shí)現(xiàn)。W^X 完全受支持,默認(rèn)情況下為 macOS Arm64 啟用,并在其他環(huán)境中選擇加入。CET 是所有環(huán)境的選擇和預(yù)覽。我們希望默認(rèn)情況下為 .NET 7 中的所有環(huán)境啟用這兩種技術(shù)。

ARM64

如今,對(duì)于筆記本電腦、云硬件和其他設(shè)備,Arm64 令人興奮不已。我們對(duì) .NET 團(tuán)隊(duì)也感到同樣的興奮,并正在盡最大努力跟上這一行業(yè)趨勢。我們直接與 Arm Holdings、Apple 和 Microsoft 的工程師合作,以確保我們的實(shí)施正確且經(jīng)過優(yōu)化,并且我們的計(jì)劃保持一致。這些密切的伙伴關(guān)系對(duì)我們幫助很大。

  • 特別感謝 Apple 在 M1 芯片發(fā)布之前向我們的團(tuán)隊(duì)發(fā)送了一蒲式耳的 Arm64 開發(fā)套件,并提供了重要的技術(shù)支持。

  • 特別感謝 Arm Holdings,他們的工程師代碼審查了我們的 Arm64 更改并進(jìn)行了性能改進(jìn)。

在此之前,我們通過 .NET Core 3.0 和 Arm32 添加了對(duì) Arm64 的初始支持。該團(tuán)隊(duì)在最近的幾個(gè)版本中都對(duì) Arm64 進(jìn)行了重大投資,并且在可預(yù)見的未來還將繼續(xù)。在 .NET 6 中,我們的主要重點(diǎn)是在macOS 和 Windows Arm64 操作系統(tǒng)上支持新的 Apple Silicon 芯片和x64 仿真場景。

您可以在 macOS 11+ 和 Windows 11+ Arm64 操作系統(tǒng)上安裝 Arm64 和 x64 版本的 .NET。我們必須做出多項(xiàng)設(shè)計(jì)選擇和產(chǎn)品更改以確保有效。

我們的策略是“親原生架構(gòu)”。我們建議您始終使用與原生架構(gòu)匹配的 SDK,即 macOS 和 Windows Arm64 上的 Arm64 SDK。SDK 是一個(gè)龐大的軟件體。與仿真相比,在 Arm64 芯片上本地運(yùn)行的性能要高得多。我們已更新 CLI 以簡化此操作。我們永遠(yuǎn)不會(huì)專注于優(yōu)化模擬 x64。

默認(rèn)情況下,如果您dotnet run使用 Arm64 SDK 的 .NET 6 應(yīng)用程序,它將作為 Arm64 運(yùn)行。您可以使用參數(shù)輕松切換到以 x64 運(yùn)行,例如. 相同的論點(diǎn)適用于其他 CLI 動(dòng)詞。有關(guān)詳細(xì)信息,請(qǐng)參閱適用于 macOS 和 Windows Arm64 的 .NET 6 RC2 更新。-adotnet run -a x64

我想確保涵蓋了一個(gè)微妙之處。當(dāng)您使用 時(shí),SDK 仍以 Arm64 的形式在本機(jī)運(yùn)行。.NET SDK體系結(jié)構(gòu)中存在進(jìn)程邊界存在的固定點(diǎn)。大多數(shù)情況下,一個(gè)進(jìn)程必須全是 Arm64 或全是 x64。我稍微簡化了一點(diǎn),但 .NET CLI 會(huì)等待 SDK 架構(gòu)中的最后一個(gè)進(jìn)程創(chuàng)建,然后將它作為您請(qǐng)求的芯片架構(gòu)啟動(dòng),例如 x64。這就是您的代碼運(yùn)行的過程。這樣,您作為開發(fā)人員可以獲得 Arm64 的好處,但您的代碼可以在它需要的過程中運(yùn)行。這僅在您需要以 x64 格式運(yùn)行某些代碼時(shí)才相關(guān)。如果你不這樣做,那么你可以一直以 Arm64 的方式運(yùn)行一切,這很好。-a x64

Arm64 支持

以下是您需要了解的關(guān)鍵點(diǎn),適用于 macOS 和 Windows Arm64:

  • 支持并推薦 .NET 6 Arm64 和 x64 SDK。

  • 支持所有支持的 Arm64 和 x64 運(yùn)行時(shí)。

  • .NET Core 3.1 和 .NET 5 SDK 可以工作,但提供的功能較少,并且在某些情況下不完全受支持。

  • dotnet test尚不能與 x64 仿真一起正常工作。我們正在為此努力。dotnet test將作為 6.0.200 版本的一部分進(jìn)行改進(jìn),可能更早。

有關(guān)更完整的信息,請(qǐng)參閱.NET 對(duì) macOS 和 Windows Arm64 的支持。

本次討論中缺少 Linux。它不像 macOS 和 Windows 那樣支持 x64 仿真。因此,這些新的 CLI 特性和支持方法并不直接適用于 Linux,Linux 也不需要它們。

Windows  Arm64

我們有一個(gè)簡單的工具來演示.NET 運(yùn)行的環(huán)境

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 上本機(jī)運(yùn)行。我將向您展示http://ASP.NETCore 的外觀。





macOS Arm64

并且您可以看到在 macOS Arm64 上的體驗(yàn)是相似的,并且還展示了架構(gòu)定位。

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 執(zhí)行是 Arm64 SDK 的默認(rèn)執(zhí)行,以及使用參數(shù)在面向 Arm64 和 x64 之間切換是多么容易。完全相同的體驗(yàn)適用于 Windows Arm64。-a





此圖像演示了相同的內(nèi)容,但使用http://ASP.NETCore。我使用的 .NET 6 Arm64 SDK 與您在上圖中看到的相同。

Arm64 上的 Docker

Docker 支持在本機(jī)架構(gòu)和仿真中運(yùn)行的容器,本機(jī)架構(gòu)是默認(rèn)的。這看起來很明顯,但當(dāng)大多數(shù) Docker Hub 目錄面向 x64 時(shí)可能會(huì)令人困惑。您可以使用來請(qǐng)求 x64 圖像。--platform linux/amd64

我們僅支持在 Arm64 操作系統(tǒng)上運(yùn)行 Linux Arm64 .NET 容器映像。這是因?yàn)槲覀儚膩聿恢С衷?a target="_blank" rel="nofollow noreferrer" data-za-detail-view-id="1043">QEMU 中運(yùn)行 .NET ,這是 Docker 用于架構(gòu)模擬的??磥磉@可能是由于 QEMU 的限制。





此圖片展示了我們維護(hù)控制臺(tái)例子:。這是一個(gè)有趣的示例,因?yàn)樗恍┯糜诖蛴?CPU 和內(nèi)存限制信息的基本邏輯,您可以使用它們。我展示的圖像設(shè)置了 CPU 和內(nèi)存限制。mcr.microsoft.com/dotnet/samples

自己試試: docker run --rm mcr.microsoft.com/dotnet/samples

Arm64 性能

Apple Silicon 和 x64 仿真支持項(xiàng)目非常重要,但是,我們也普遍提高了 Arm64 性能。





此圖展示了將堆棧幀的內(nèi)容清零方面的改進(jìn),這是一種常見操作。綠線是新行為,而橙色線是另一個(gè)(不太有益的)實(shí)驗(yàn),兩者都相對(duì)于基線有所改進(jìn),由藍(lán)線表示。對(duì)于這個(gè)測試,越低越好。

容器

.NET 6 更適合容器,主要基于本文中討論的所有改進(jìn),適用于 Arm64 和 x64。我們還進(jìn)行了關(guān)鍵更改,這將有助于各種場景。使用 .NET 6 驗(yàn)證容器改進(jìn)演示了其中一些改進(jìn)正在一起測試。

Windows 容器改進(jìn)和新的環(huán)境變量也已包含在 11 月 9 日(明天)發(fā)布的 11 月 .NET Framework 4.8 容器更新中。

發(fā)行說明可在我們的 docker 存儲(chǔ)庫中找到:


Windows Docker

.NET 6 添加了對(duì) Windows 進(jìn)程隔離容器的支持。如果您在 Azure Kubernetes 服務(wù) (AKS) 中使用Windows 容器,則您依賴于進(jìn)程隔離的容器。進(jìn)程隔離容器可以被認(rèn)為與 Linux 容器非常相似。Linux 容器使用cgroups,Windows 進(jìn)程隔離容器使用Job Objects。Windows 還提供 Hyper-V 容器,它通過更大的虛擬化提供更大的隔離。Hyper-V 容器在 .NET 6 中沒有變化。

此更改的主要價(jià)值是現(xiàn)在將報(bào)告 Windows 進(jìn)程隔離容器的正確值。如果在 64 核機(jī)器上創(chuàng)建 2 核容器,將返回. 在以前的版本中,此屬性將報(bào)告機(jī)器上的處理器總數(shù),與 Docker CLI、Kubernetes 或其他容器編排器/運(yùn)行時(shí)指定的限制無關(guān)。該值被 .NET 的各個(gè)部分用于縮放目的,包括 .NET 垃圾收集器(盡管它依賴于相關(guān)的較低級(jí)別的 API)。社區(qū)庫也依賴此 API 進(jìn)行擴(kuò)展。Environment.ProcessorCountEnvironment.ProcessorCount2

我們最近在 AKS 上使用大量 Pod 生產(chǎn)的 Windows 容器上與客戶一起驗(yàn)證了這項(xiàng)新功能。他們能夠用50%的內(nèi)存運(yùn)行成功(相比,其典型的配置),先前導(dǎo)致了水平OutOfMemoryException和StackOverflowException例外。他們沒有花時(shí)間找到最小內(nèi)存配置,但我們猜測它明顯低于他們典型內(nèi)存配置的 50%。由于這一變化,他們將轉(zhuǎn)向更便宜的 Azure 配置,從而節(jié)省資金。這是一個(gè)不錯(cuò)的、輕松的勝利,只需升級(jí)即可。

優(yōu)化縮放

我們從用戶那里聽到一些應(yīng)用程序在報(bào)告正確值時(shí)無法實(shí)現(xiàn)最佳縮放。如果這聽起來與您剛剛閱讀的 Windows Containers 內(nèi)容相反,那么它有點(diǎn)像。.NET 6 現(xiàn)在提供DOTNET_PROCESSOR_COUNT 環(huán)境變量來手動(dòng)控制 的值。在典型用例中,應(yīng)用程序可能在 64 核機(jī)器上配置有 4 個(gè)核,并且在 8 核或 16 核方面具有最佳擴(kuò)展性。此環(huán)境變量可用于啟用該縮放。Environment.ProcessorCountEnvironment.ProcessorCount

這個(gè)模型可能看起來很奇怪,其中和(通過 Docker CLI)值可能不同。默認(rèn)情況下,容器運(yùn)行時(shí)面向核心等效項(xiàng),而不是實(shí)際核心。這意味著,當(dāng)您說需要 4 個(gè)內(nèi)核時(shí),您將獲得 4 個(gè)內(nèi)核的等效 CPU 時(shí)間,但是您的應(yīng)用程序可能(理論上)在更多內(nèi)核上運(yùn)行,甚至在短時(shí)間內(nèi)在 64 核機(jī)器上運(yùn)行所有 64 個(gè)內(nèi)核。這可能會(huì)使您的應(yīng)用程序在 4 個(gè)以上的線程上更好地?cái)U(kuò)展(繼續(xù)示例),并且分配更多可能是有益的。這假設(shè)線程分配基于 的值。如果您選擇設(shè)置更高的值,您的應(yīng)用可能會(huì)使用更多內(nèi)存。對(duì)于某些工作負(fù)載,這是一個(gè)簡單的權(quán)衡。至少,這是一個(gè)您可以測試的新選項(xiàng)。Environment.ProcessorCount--cpusEnvironment.ProcessorCount

Linux 和 Windows 容器均支持此新功能。

Docker 還提供了 CPU 組功能,您的應(yīng)用程序可以關(guān)聯(lián)到特定的內(nèi)核。在這種情況下不推薦使用此功能,因?yàn)閼?yīng)用程序可以訪問的內(nèi)核數(shù)量是具體定義的。我們還看到了將它與 Hyper-V 容器一起使用時(shí)的一些問題,它并不是真正適用于這種隔離模式。

Debian 11 “靶心”

我們密切關(guān)注Linux 發(fā)行版的生命周期和發(fā)布計(jì)劃,并嘗試代表您做出最佳選擇。Debian 是我們用于默認(rèn) Linux 映像的 Linux 發(fā)行版。如果您6.0從我們的一個(gè)容器存儲(chǔ)庫中提取標(biāo)簽,您將提取一個(gè) Debian 映像(假設(shè)您使用的是 Linux 容器)。對(duì)于每個(gè)新的 .NET 版本,我們都會(huì)考慮是否應(yīng)該采用新的 Debian 版本。

作為政策問題,我們不會(huì)為了我們的便利標(biāo)簽而更改 Debian 版本,例如6.0中期發(fā)布。如果我們這樣做了,某些應(yīng)用程序肯定會(huì)崩潰。這意味著,在發(fā)布之初選擇 Debian 版本非常重要。此外,這些圖像得到了很多使用,主要是因?yàn)樗鼈儽弧昂脴?biāo)簽”引用。

Debian 和 .NET 版本自然不是一起計(jì)劃的。當(dāng)我們開始 .NET 6 時(shí),我們看到 Debian “bullseye”可能會(huì)在 2021 年發(fā)布。我們決定從發(fā)布之初就押注于 Bullseye。我們開始使用.NET 6 Preview 1發(fā)布基于 Bullseye 的容器映像,并決定不再回頭。賭注是 .NET 6 版本將在與 Bullseye 版本的競爭中失敗。到 8 月 8 日,我們?nèi)匀徊恢?Bullseye 什么時(shí)候發(fā)貨,在我們自己的版本發(fā)布前三個(gè)月,即 11 月 8 日。我們不想在預(yù)覽版 Linux 上發(fā)布生產(chǎn) .NET 6,但我們堅(jiān)持到了我們會(huì)輸?shù)暨@場比賽的計(jì)劃。

當(dāng)Debian 11 “bullseye”于 8 月 14 日發(fā)布時(shí),我們感到驚喜。我們輸了比賽,但贏了賭注。這意味著 .NET 6 用戶從第一天起就默認(rèn)獲得最好和最新的 Debian。我們相信 Debian 11 和 .NET 6 將成為許多用戶的絕佳組合。對(duì)不起,克星,我們撞到了靶心。

較新的發(fā)行版在其軟件包源中包含各種軟件包的較新主要版本,并且通??梢愿斓孬@得CVE 修復(fù)。這是對(duì)較新內(nèi)核的補(bǔ)充。新的發(fā)行版可以更好地為用戶服務(wù)。

展望未來,不久我們將開始計(jì)劃對(duì)Ubuntu 22.04 的支持。Ubuntu是另一個(gè) Debian 家族發(fā)行版,深受 .NET 開發(fā)人員的歡迎。我們希望為新的 Ubuntu LTS 版本提供當(dāng)日支持。

Tianon Gravi 致敬,他為社區(qū)維護(hù) Debian 映像并在我們遇到問題時(shí)幫助我們。

網(wǎng)絡(luò)監(jiān)視器

dotnet monitor是容器的重要診斷工具。它作為 sidecar 容器鏡像已經(jīng)有一段時(shí)間了,但處于不受支持的“實(shí)驗(yàn)”狀態(tài)。作為 .NET 6 的一部分,我們將發(fā)布一個(gè)完全支持生產(chǎn)的基于 .NET 6 的dotnet monitor映像。

dotnet monitor已被 Azure 應(yīng)用服務(wù)用作其http://ASP.NETCore Linux 診斷體驗(yàn)的實(shí)現(xiàn)細(xì)節(jié)。這是預(yù)期的場景之一,建立在 dotnet monitor 之上以提供更高級(jí)別和更高價(jià)值的體驗(yàn)。

您現(xiàn)在可以拉取新圖像:

docker pull mcr.microsoft.com/dotnet/monitor:6.0

dotnet monitor使從 .NET 進(jìn)程訪問診斷信息(日志、跟蹤、進(jìn)程轉(zhuǎn)儲(chǔ))變得更加容易。在臺(tái)式機(jī)上很容易訪問您想要的所有診斷信息,但是,例如,那些熟悉的技術(shù)在使用容器的生產(chǎn)中可能不起作用。dotnet monitor提供了一種統(tǒng)一的方法來收集這些診斷工件,無論是在您的臺(tái)式機(jī)上還是在 Kubernetes 集群中運(yùn)行。收集這些診斷工件有兩種不同的機(jī)制:

  • 用于臨時(shí)收集工件的HTTP API。當(dāng)您已經(jīng)知道您的應(yīng)用程序遇到問題并且您有興趣收集更多信息時(shí),您可以調(diào)用這些 API 端點(diǎn)。

  • 基于規(guī)則的配置觸發(fā)器,用于始終在線的工件集合。您可以配置規(guī)則以在滿足所需條件時(shí)收集診斷數(shù)據(jù),例如,在持續(xù)高 CPU 時(shí)收集進(jìn)程轉(zhuǎn)儲(chǔ)。

dotnet monitor為 .NET 應(yīng)用程序提供了一個(gè)通用的診斷 API,可以使用任何工具在任何地方使用?!巴ㄓ?API”不是 .NET API,而是您可以調(diào)用和查詢的 Web API。dotnet monitor包括一個(gè) ASP.NET Web 服務(wù)器,它直接與 .NET 運(yùn)行時(shí)中的診斷服務(wù)器交互并公開數(shù)據(jù)。的設(shè)計(jì)dotnet monitor支持生產(chǎn)中的高性能監(jiān)控和安全使用,以控制對(duì)特權(quán)信息的訪問。dotnet monitor通過非互聯(lián)網(wǎng)可尋址的unix 域套接字與運(yùn)行時(shí)交互——跨越容器邊界。該模型通信模型非常適合此用例。

結(jié)構(gòu)化JSON 日志

JSON格式現(xiàn)在是默認(rèn)控制臺(tái)記錄f="https://hub.docker.com/_/microsoft-dotnet-aspnet">aspnet.NET 6容器圖像。.NET 5 中的默認(rèn)設(shè)置為簡單的控制臺(tái)格式化程序。進(jìn)行此更改是為了擁有一個(gè)默認(rèn)配置,該配置可與依賴于機(jī)器可讀格式(如 JSON)的自動(dòng)化工具配合使用。

圖像的輸出現(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}"}}

可以通過設(shè)置或取消設(shè)置Logging__Console__FormatterName環(huán)境變量或通過代碼更改來更改記錄器格式類型(有關(guān)更多詳細(xì)信息,請(qǐng)參閱控制臺(tái)日志格式)。

更改后,您將看到如下輸出(就像 .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

注意:此更改不會(huì)影響開發(fā)人員計(jì)算機(jī)上的 .NET SDK,與dotnet run. 此更改特定于aspnet容器映像。

支持 OpenTelemetry 指標(biāo)

我們一直在為最近的幾個(gè) .NET 版本添加對(duì) OpenTelemetry 的支持,作為我們對(duì)可觀察性的關(guān)注的一部分。在 .NET 6 中,我們添加了對(duì)OpenTelemetry Metrics API 的支持。通過添加對(duì) OpenTelemetry 的支持,您的應(yīng)用程序可以與其他OpenTelemetry系統(tǒng)無縫互操作。

System.Diagnostics.MetricsOpenTelemetry Metrics API 規(guī)范的 .NET 實(shí)現(xiàn)。Metrics API 是專門為處理原始測量而設(shè)計(jì)的,目的是高效且同時(shí)地生成這些測量的連續(xù)摘要。

API 包括Meter可用于創(chuàng)建儀器對(duì)象的類。這些API暴露四個(gè)儀器類:Counter,Histogram,ObservableCounter,并ObservableGauge支持不同的指標(biāo)方案。此外,API 公開MeterListener該類以允許偵聽儀器的記錄測量以進(jìn)行聚合和分組。

OpenTelemetry .NET實(shí)現(xiàn)將擴(kuò)展到使用這些新的API,其中新增的指標(biāo)可觀察方案的支持。

庫測量記錄示例

    Meter meter = new Meter("io.opentelemetry.contrib.mongodb", "v1.0");
    Counter counter = meter.CreateCounter("Requests");
    counter.Add(1);
    counter.Add(1, KeyValuePair.Create("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((instrument, measurement, tags, state) =>
    {
        Console.WriteLine($"Instrument: {instrument.Name} has recorded the measurement {measurement}");
    });
    listener.Start();

Windows 窗體

我們繼續(xù)在 Windows 窗體中進(jìn)行關(guān)鍵改進(jìn)。.NET 6 包括更好的控件可訪問性、設(shè)置應(yīng)用程序范圍默認(rèn)字體的能力、模板更新等。

輔助功能改進(jìn)

在此版本中,我們?cè)黾恿?a target="_blank" rel="nofollow noreferrer" data-za-detail-view-id="1043">UIA提供商為CheckedListBox,LinkLabel,Panel,ScrollBar,TabControl和TrackBar,使像旁白工具和測試自動(dòng)化與應(yīng)用程序的元素進(jìn)行交互。

默認(rèn)字體

現(xiàn)在,您可以設(shè)置默認(rèn)字體的應(yīng)用程序有。Application.SetDefaultFont

void Application.SetDefaultFont(Font font)

最少的應(yīng)用

以下是帶有 .NET 6最小 Windows 窗體應(yīng)用程序

class Program
{
    [STAThread]
    static void Main()
    {
        ApplicationConfiguration.Initialize();
        Application.Run(new Form1());
    }
}

作為 .NET 6 版本的一部分,我們一直在更新大多數(shù)模板,使其更加現(xiàn)代和簡約,包括 Windows 窗體。我們決定讓 Windows 窗體模板更傳統(tǒng)一些,部分原因是需要將該屬性應(yīng)用于應(yīng)用程序入口點(diǎn)。然而,除了立即出現(xiàn)之外,還有更多的玩法。[STAThread]

ApplicationConfiguration.Initialize() 是一個(gè)源代碼生成的 API,它在后臺(tái)發(fā)出以下調(diào)用:

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.SetDefaultFont(new Font(...));
Application.SetHighDpiMode(HighDpiMode.SystemAware);

這些調(diào)用的參數(shù)可通過csproj 或 props 文件中的MSBuild 屬性進(jìn)行配置。

Visual Studio 2022 中的 Windows 窗體設(shè)計(jì)器也知道這些屬性(目前它只讀取默認(rèn)字體),并且可以向您展示您的應(yīng)用程序,就像它在運(yùn)行時(shí)一樣:





模板更新

C# 的 Windows 窗體模板已更新,以支持新的應(yīng)用程序引導(dǎo)程序、指令、文件范圍的命名空間和可為空的引用類型。global using

更多運(yùn)行時(shí)設(shè)計(jì)器

現(xiàn)在您可以構(gòu)建通用設(shè)計(jì)器(例如,報(bào)表設(shè)計(jì)器),因?yàn)?.NET 6 具有設(shè)計(jì)器和設(shè)計(jì)器相關(guān)基礎(chǔ)結(jié)構(gòu)的所有缺失部分。有關(guān)更多信息,請(qǐng)參閱此博客文章。

單文件應(yīng)用程序

在 .NET 6 中,已為 Windows 和 macOS 啟用內(nèi)存中單文件應(yīng)用程序。在 .NET 5 中,這種部署類型僅限于 Linux。您現(xiàn)在可以為所有支持的操作系統(tǒng)發(fā)布作為單個(gè)文件部署和啟動(dòng)的單文件二進(jìn)制文件。單文件應(yīng)用程序不再將任何核心運(yùn)行時(shí)程序集提取到臨時(shí)目錄。

這種擴(kuò)展功能基于稱為“超級(jí)主機(jī)”的構(gòu)建塊?!癮pphost”是在非單文件情況下啟動(dòng)應(yīng)用程序的可執(zhí)行文件,例如或。Apphost 包含用于查找運(yùn)行時(shí)、加載它并使用該運(yùn)行時(shí)啟動(dòng)您的應(yīng)用程序的代碼。Superhost 仍然執(zhí)行其中一些任務(wù),但使用所有 CoreCLR 本機(jī)二進(jìn)制文件的靜態(tài)鏈接副本。靜態(tài)鏈接是我們用來啟用單個(gè)文件體驗(yàn)的方法。myapp.exe./myapp

本機(jī)依賴項(xiàng)(如 NuGet 包附帶的依賴項(xiàng))是單文件嵌入的顯著例外。默認(rèn)情況下,它們不包含在單個(gè)文件中。例如,WPF 本機(jī)依賴項(xiàng)不是超級(jí)主機(jī)的一部分,導(dǎo)致除了單文件應(yīng)用程序之外還有其他文件。您可以使用該設(shè)置IncludeNativeLibrariesForSelfExtract來嵌入和提取本機(jī)依賴項(xiàng)。

靜態(tài)分析

我們改進(jìn)了單文件分析器以允許自定義警告。如果您的 API 在單文件發(fā)布中不起作用,您現(xiàn)在可以使用該屬性對(duì)其進(jìn)行標(biāo)記,如果啟用了分析器,則會(huì)出現(xiàn)警告。添加該屬性還將使方法中與單個(gè)文件相關(guān)的所有警告靜音,因此您可以使用該警告將警告向上傳播到您的公共 API。[RequiresAssemblyFiles]

設(shè)置為 時(shí),會(huì)自動(dòng)為exe項(xiàng)目啟用單文件分析器,但您也可以通過設(shè)置為為任何項(xiàng)目啟用它。如果您想支持庫作為單個(gè)文件應(yīng)用程序的一部分,這會(huì)很有幫助。PublishSingleFiletrueEnableSingleFileAnalysistrue

在 .NET 5 中,我們添加了警告和其他一些在單文件包中表現(xiàn)不同的 API。Assembly.Location

壓縮

單文件包現(xiàn)在支持壓縮,可以通過將屬性設(shè)置EnableCompressionInSingleFile為true. 在運(yùn)行時(shí),文件會(huì)根據(jù)需要解壓縮到內(nèi)存中。壓縮可以為某些場景提供巨大的空間節(jié)省。

讓我們看一下與NuGet 包資源管理器一起使用的帶壓縮和不帶壓縮的單個(gè)文件發(fā)布。

無壓縮:172 MB





壓縮后:71.6 MB





壓縮可以顯著增加應(yīng)用程序的啟動(dòng)時(shí)間,尤其是在 Unix 平臺(tái)上。Unix 平臺(tái)具有無法與壓縮一起使用的無復(fù)制快速啟動(dòng)路徑。您應(yīng)該在啟用壓縮后測試您的應(yīng)用,看看額外的啟動(dòng)成本是否可以接受。

單文件調(diào)試

單文件應(yīng)用程序目前只能使用平臺(tái)調(diào)試器(如 WinDBG)進(jìn)行調(diào)試。我們正在考慮在 Visual Studio 2022 的更高版本中添加 Visual Studio 調(diào)試。

macOS 上的單文件簽名

單文件應(yīng)用程序現(xiàn)在滿足 macOS 上的 Apple 公證和簽名要求。在具體的變化涉及到我們?cè)陔x散文件布局方面構(gòu)建單個(gè)文件的應(yīng)用程序的方式。

蘋果開始執(zhí)行新規(guī)定簽署和公證MacOS的卡特琳娜。我們一直在與 Apple 密切合作,以了解要求,并尋找解決方案,使 .NET 等開發(fā)平臺(tái)能夠在該環(huán)境中良好運(yùn)行。在最近的幾個(gè) .NET 版本中,我們已經(jīng)對(duì)產(chǎn)品進(jìn)行了更改并記錄了用戶工作流程,以滿足 Apple 的要求。剩下的差距之一是單文件簽名,這是在 macOS 上分發(fā) .NET 應(yīng)用程序的要求,包括在 macOS 商店中。

IL 修整

該團(tuán)隊(duì)一直致力于為多個(gè)版本進(jìn)行 IL 修整。.NET 6 代表了這一旅程的重要一步。我們一直在努力使更激進(jìn)的修剪模式安全且可預(yù)測,因此有信心將其設(shè)為默認(rèn)值。以前是選擇加入功能,現(xiàn)在是默認(rèn)功能。TrimMode=link

我們有一個(gè)三管齊下的修剪策略:

  • 提高平臺(tái)的修剪能力。

  • 對(duì)平臺(tái)進(jìn)行注釋以提供更好的警告并使其他人也能這樣做。

  • 在此基礎(chǔ)上,使默認(rèn)修剪模式更加激進(jìn),以便輕松將應(yīng)用程序變小。

由于使用未注釋反射的應(yīng)用程序的結(jié)果不可靠,因此修剪之前一直處于預(yù)覽狀態(tài)。有了修剪警告,體驗(yàn)現(xiàn)在應(yīng)該是可預(yù)測的。沒有修剪警告的應(yīng)用程序應(yīng)該正確修剪并且在運(yùn)行時(shí)觀察到行為沒有變化。目前,只有核心 .NET 庫已經(jīng)完全注釋用于修剪,但我們希望看到生態(tài)系統(tǒng)注釋用于修剪并成為修剪兼容

減少應(yīng)用程序大小

讓我們來看看使用crossgen 的這種修剪改進(jìn),它是 SDK 工具之一。可以只用一些修剪警告來修剪它,crossgen 團(tuán)隊(duì)能夠解決這些問題。

首先,讓我們將 crossgen 發(fā)布為一個(gè)獨(dú)立的應(yīng)用程序,無需修剪。它是 80 MB(包括 .NET 運(yùn)行時(shí)和所有庫)。





然后我們可以嘗試(現(xiàn)在是舊版).NET 5 默認(rèn)修剪模式,copyused. 結(jié)果下降到 55 MB。





新的 .NET 6 默認(rèn)修剪模式link將自包含文件大小進(jìn)一步降低到 36MB。





我們希望新的link修剪模式能更好地滿足修剪的期望:顯著的節(jié)省和可預(yù)測的結(jié)果。

默認(rèn)啟用警告

修剪警告告訴您修剪可能會(huì)刪除運(yùn)行時(shí)使用的代碼的地方。這些警告以前在默認(rèn)情況下被禁用,因?yàn)榫娣浅`须s,主要是由于 .NET 平臺(tái)沒有作為第一類場景參與修剪。

我們對(duì) .NET 庫的大部分進(jìn)行了注釋,以便它們生成準(zhǔn)確的修剪警告。因此,我們認(rèn)為是時(shí)候默認(rèn)啟用修剪警告了。http://ASP.NETCore 和 Windows 桌面運(yùn)行時(shí)庫尚未注釋。我們計(jì)劃接下來(.NET 6 之后)注釋http://ASP.NET服務(wù)組件。我們希望看到社區(qū)在 .NET 6 發(fā)布后對(duì) NuGet 庫進(jìn)行注釋。

您可以通過設(shè)置為來禁用警告true。

更多信息:

與本機(jī) AOT 共享

我們也為Native AOT 實(shí)驗(yàn)實(shí)施了相同的修剪警告,這應(yīng)該會(huì)以大致相同的方式改善 Native AOT 編譯體驗(yàn)。

數(shù)學(xué)

我們顯著改進(jìn)了數(shù)學(xué) API。社區(qū)一些人已經(jīng)在享受這些改進(jìn)

面向性能的 API

面向性能的數(shù)學(xué) API 已添加到 System.Math。如果底層硬件支持,它們的實(shí)現(xiàn)是硬件加速的。

新 API:

  • SinCos用于同時(shí)計(jì)算Sin和Cos。

  • ReciprocalEstimate用于計(jì)算 的近似值。1 / x

  • ReciprocalSqrtEstimate用于計(jì)算 的近似值。1 / Sqrt(x)

新的重載:

  • Clamp、DivRem、Min、 和Max支持nint和nuint。

  • Abs和Sign支持nint。

  • DivRem返回 a 的變體tuple。

性能改進(jìn):

大整數(shù)性能

從十進(jìn)制和十六進(jìn)制字符串解析 BigIntegers已得到改進(jìn)。我們看到了高達(dá) 89% 的改進(jìn),如下圖所示(越低越好)。





歸功于約瑟夫·達(dá)席爾瓦。

Complex API 現(xiàn)在注釋為 readonly

href="https://github.com/dotnet/runtime/pull/51797/">現(xiàn)在System.Numerics.Complexreadonly對(duì)各種API 進(jìn)行了注釋,以確保不會(huì)為readonly通過in.

感謝hrrrrustic。

BitConverter 現(xiàn)在支持浮點(diǎn)到無符號(hào)整數(shù)比特轉(zhuǎn)換

BitConverter ref="https://github.com/dotnet/runtime/pull/53784">現(xiàn)在支持DoubleToUInt64Bits,HalfToUInt16Bits,SingleToUInt32Bits,UInt16BitsToHalf,UInt32BitsToSingle,和UInt64BitsToDouble。這應(yīng)該可以在需要時(shí)更容易地進(jìn)行浮點(diǎn)位操作。

感謝米哈爾Petryka。

BitOperations 支持附加功能

BitOperations現(xiàn)在支持IsPow2、RoundUpToPowerOf2為現(xiàn)有函數(shù)href="https://github.com/dotnet/runtime/pull/58733">提供nint/nuint重載。

感謝約翰·凱利,霍耀元羅賓·林德納。

Vector, Vector2, Vector3, 和Vector4改進(jìn)

Vectorref="https://github.com/dotnet/runtime/pull/50832">現(xiàn)在支持nintnuintC# 9 中添加原始類型。例如,此更改應(yīng)該可以更輕松地使用帶有指針或平臺(tái)相關(guān)長度類型的 SIMD 指令。

Vectorf="https://github.com/dotnet/runtime/pull/53527">現(xiàn)在支持一種Sum方法來簡化需要計(jì)算向量中所有元素的“水平總和”。感謝伊萬茲拉塔諾夫。

Vector"https://githhttp://ub.com/dotnet/runtime/pull/47150">現(xiàn)在支持通用方法As來簡化在具體類型未知的通用上下文中處理向量。感謝霍耀元

ref="https://github.com/dotnet/runtime/pull/50062">重載支持Span已添加到Vector2、Vector3和Vector4以改善需要加載或存儲(chǔ)向量類型時(shí)的體驗(yàn)。

更好地解析標(biāo)準(zhǔn)數(shù)字格式

我們改進(jìn)了標(biāo)準(zhǔn)數(shù)字類型的解析器,特別是和。他們現(xiàn)在將理解精度要求 > 99 位小數(shù),并將提供精確到那么多位數(shù)的結(jié)果。此外,解析器現(xiàn)在更好地支持方法中的尾隨零。.ToString.TryFormatParse

以下示例演示了前后行為。

  • 32.ToString("C100") -> C132

    • .NET 6: $32.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

    • .NET 5:我們?cè)诟袷交a中存在人為限制,只能處理 <= 99="">= 100,我們將輸入解釋為自定義格式。


  • 32.ToString("H99") -> 扔一個(gè) FormatException

    • .NET 6:拋出 FormatException

    • 這是正確的行為,但在此處調(diào)用它是為了與下一個(gè)示例進(jìn)行對(duì)比。


  • 32.ToString("H100") -> H132

    • .NET 6:拋出 FormatException

    • .NET 5:H是無效的格式說明符。所以,我們應(yīng)該拋出一個(gè)FormatException. 相反,我們將精度 >= 100 解釋為自定義格式的錯(cuò)誤行為意味著我們返回了錯(cuò)誤的值。


  • double.Parse("9007199254740997.0") -> 9007199254740998

    • .NET 6: 9007199254740996.

    • .NET 5:9007199254740997.0不能完全以 IEEE 754 格式表示。使用我們當(dāng)前的舍入方案,正確的返回值應(yīng)該是9007199254740996。但是,輸入的最后一部分迫使解析器錯(cuò)誤地舍入結(jié)果并返回。.09007199254740998


系統(tǒng).文本.Json

System.Text.Json提供了多種高性能的 API 來處理 JSON 文檔。在過去的幾個(gè)版本中,我們添加了新功能,以進(jìn)一步提高 JSON 處理性能并減輕想要從. 此版本包括在這條道路上的繼續(xù),并且是性能的重大進(jìn)步,特別是在序列化器源生成器方面。NewtonSoft.Json

JsonSerializer 源代碼生成

注意:應(yīng)重新編譯使用 .NET 6 RC1 或更早版本的源代碼生成的應(yīng)用程序。

幾乎所有 .NET 序列化程序的支柱都是反射。反射對(duì)于某些場景來說是一項(xiàng)很棒的功能,但不能作為高性能云原生應(yīng)用程序(通常(反)序列化和處理大量 JSON 文檔)的基礎(chǔ)。反射是啟動(dòng)、內(nèi)存使用和程序集修整的問題。

運(yùn)行時(shí)反射的替代方案是編譯時(shí)源代碼生成。在 .NET 6 中,我們將一個(gè)"https://devblogs.microsoft.com/dotnet/try-the-new-system-text-json-source-generator/">新的源生成器作為System.Text.Json. JSON 源生成器可與JsonSerializer多種方式結(jié)合使用并可進(jìn)行配置。

它可以提供以下好處:

  • 減少啟動(dòng)時(shí)間

  • 提高序列化吞吐量

  • 減少私有內(nèi)存使用

  • 刪除運(yùn)行時(shí)使用和System.ReflectionSystem.Reflection.Emit

  • IL 修整兼容性

默認(rèn)情況下,JSON 源生成器為給定的可序列化類型發(fā)出序列化邏輯。JsonSerializer通過生成Utf8JsonWriter直接使用的源代碼,這提供了比使用現(xiàn)有方法更高的性能。簡而言之,源代碼生成器提供了一種在編譯時(shí)為您提供不同實(shí)現(xiàn)的方法,以便使運(yùn)行時(shí)體驗(yàn)更好。

給定一個(gè)簡單類型:

namespace Test
{
    internal class JsonMessage
    {
        public string Message { get; set; }
    }
}

源生成器可以配置為為示例JsonMessage類型的實(shí)例生成序列化邏輯。請(qǐng)注意,類名JsonContext是任意的。您可以為生成的源使用任何您想要的類名。

using System.Text.Json.Serialization;

namespace Test
{
    [JsonSerializable(typeof(JsonMessage)]
    internal partial class JsonContext : JsonSerializerContext
    {
    }
}

使用此模式的序列化程序調(diào)用可能類似于以下示例。此示例提供了可能的最佳性能。

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根據(jù)您的反饋,未來可能會(huì)提供類似的反序列化支持——基于——。

源生成器還發(fā)出類型元數(shù)據(jù)初始化邏輯,這也有利于反序列化。要反序列化JsonMessage使用預(yù)生成類型元數(shù)據(jù)的實(shí)例,您可以執(zhí)行以下操作:

JsonSerializer.Deserialize(json, JsonContext.Default.JsonMessage);

JsonSerializer 支持 IAsyncEnumerable

現(xiàn)在,可以(反)序列化JSON陣列IAsyncEnumerable與。以下示例使用流作為數(shù)據(jù)的任何異步源的表示。源可以是本地機(jī)器上的文件,也可以是數(shù)據(jù)庫查詢或 Web 服務(wù) API 調(diào)用的結(jié)果。System.Text.Json

JsonSerializer.SerializeAsync已更新以識(shí)別IAsyncEnumerable值并提供特殊處理。

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

static async IAsyncEnumerable 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值僅支持使用異步序列化方法。嘗試使用同步方法進(jìn)行序列化將導(dǎo)致NotSupportedException拋出異常。

流式反序列化需要一個(gè)新的 API 返回. 我們?yōu)榇四康奶砑恿嗽摲椒?,您可以在以下示例中看到。IAsyncEnumerableJsonSerializer.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(stream))
{
    Console.WriteLine(item);
}

此示例將按需反序列化元素,并且在使用特別大的數(shù)據(jù)流時(shí)非常有用。它只支持從根級(jí) JSON 數(shù)組中讀取,盡管將來可能會(huì)根據(jù)反饋放寬。

現(xiàn)有DeserializeAsync方法名義上支持,但在其非流式方法簽名的范圍內(nèi)。它必須將最終結(jié)果作為單個(gè)值返回,如下例所示。IAsyncEnumerable

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(stream);
await foreach (int item in result.Data)
{
    Console.WriteLine(item);
}

public class MyPoco
{
    public IAsyncEnumerable Data { get; set; }
}

在此示例中,反序列化器將IAsyncEnumerable在返回反序列化對(duì)象之前緩沖內(nèi)存中的所有內(nèi)容。這是因?yàn)榉葱蛄谢餍枰诜祷亟Y(jié)果之前消耗整個(gè) JSON 值。

System.Text.Json:可寫 DOM 功能

寫JSON DOM功能增加了一個(gè)新的簡單的,高性能的編程模型的。這個(gè)新的 API 很有吸引力,因?yàn)樗苊饬诵枰獜?qiáng)類型的序列化契約,并且與現(xiàn)有類型相比,DOM 是可變的。System.Text.JsonJsonDocument

這個(gè)新的 API 有以下好處:

  • 在不可能或不希望使用POCO類型的情況下,或者當(dāng) JSON 模式不固定且必須檢查時(shí),序列化的輕量級(jí)替代方案。

  • 允許對(duì)大樹的子集進(jìn)行有效修改。例如,可以高效地導(dǎo)航到大型 JSON 樹的子部分并從該子部分讀取數(shù)組或反序列化 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();
    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();
    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() == 11);

ReferenceHandler.IgnoreCycles

JsonSerializer(System.Text.Json)現(xiàn)在支持在序列化對(duì)象圖時(shí)忽略循環(huán)的能力。該選項(xiàng)的行為與 Newtonsoft.Json 相似。一個(gè)主要區(qū)別是 System.Text.Json 實(shí)現(xiàn)用JSON 標(biāo)記替換引用循環(huán),而不是忽略對(duì)象引用。ReferenceHandler.IgnoreCyclesReferenceLoopHandling.Ignorenull

您可以在以下示例中看到 的行為。在這種情況下,屬性被序列化,因?yàn)樗駝t會(huì)創(chuàng)建一個(gè)循環(huán)。ReferenceHandler.IgnoreCyclesNextnull

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}
}

源代碼構(gòu)建

使用源代碼構(gòu)建,只需使用幾條命令即可在您自己的機(jī)器上從源代碼構(gòu)建 .NET SDK。讓我解釋一下為什么這個(gè)項(xiàng)目很重要。

自 .NET Core 1.0 發(fā)布之前,源代碼構(gòu)建是一個(gè)場景,也是我們與 Red Hat 合作開發(fā)的基礎(chǔ)設(shè)施。幾年后,我們非常接近提供它的完全自動(dòng)化版本。對(duì)于 Red Hat Enterprise Linux (RHEL) .NET 用戶來說,此功能非常重要。紅帽告訴我們,.NET 已經(jīng)發(fā)展成為其生態(tài)系統(tǒng)的重要開發(fā)者平臺(tái)。好的!

Linux 發(fā)行版黃金標(biāo)準(zhǔn)是使用作為發(fā)行版存檔一部分的編譯器和工具鏈構(gòu)建開源代碼。這適用于 .NET 運(yùn)行時(shí)(用 C++ 編寫),但不適用于任何用 C# 編寫的代碼。對(duì)于 C# 代碼,我們使用兩遍構(gòu)建機(jī)制來滿足發(fā)行版要求。這有點(diǎn)復(fù)雜,但了解流程很重要。

Red Hat 使用 .NET SDK (#1) 的 Microsoft 二進(jìn)制構(gòu)建來構(gòu)建 .NET SDK 源代碼,以生成 SDK (#2) 的純開源二進(jìn)制構(gòu)建。之后,使用 SDK 的新版本 (#2) 再次構(gòu)建相同的 SDK 源代碼,以生成可證明的開源 SDK (#3)。.NET SDK (#3) 的最終二進(jìn)制版本隨后可供 RHEL 用戶使用。之后,Red Hat 可以使用相同的 SDK (#3) 來構(gòu)建新的 .NET 版本,而不再需要使用 Microsoft SDK 來構(gòu)建月度更新。

這個(gè)過程可能令人驚訝和困惑。開源發(fā)行版需要由開源工具構(gòu)建。此模式確保不需要 Microsoft 構(gòu)建的 SDK,無論是有意還是無意。作為開發(fā)者平臺(tái),被包含在發(fā)行版中比僅使用兼容許可證有更高的標(biāo)準(zhǔn)。源構(gòu)建項(xiàng)目使 .NET 能夠滿足這一要求。

源代碼構(gòu)建的交付物是一個(gè)源代碼壓縮包。源 tarball 包含 SDK(對(duì)于給定版本)的所有源。從那里,紅帽(或其他組織)可以構(gòu)建自己的 SDK 版本。Red Hat 策略需要使用從源代碼構(gòu)建的工具鏈來生成二進(jìn)制 tar 球,這就是他們使用兩遍方法的原因。但是源代碼構(gòu)建本身不需要這種兩遍方法。

在 Linux 生態(tài)系統(tǒng)中,為給定組件提供源代碼和二進(jìn)制包或 tarball 是很常見的。我們已經(jīng)有可用的二進(jìn)制 tarball,現(xiàn)在也有源 tarball。這使得 .NET 與標(biāo)準(zhǔn)組件模式相匹配。

.NET 6 的重大改進(jìn)是源 tarball 現(xiàn)在是我們構(gòu)建的產(chǎn)品。過去,它需要大量的人工來制作,這也導(dǎo)致將源 tarball 傳送到 Red Hat 的延遲很長。雙方對(duì)此都不滿意。

五年多以來,我們一直與紅帽在這個(gè)項(xiàng)目上密切合作。它取得了成功,在很大程度上要?dú)w功于我們有幸與之共事的優(yōu)秀紅帽工程師的努力。其他發(fā)行版和組織已經(jīng)并將從他們的努力中受益。

附帶說明一下,源代碼構(gòu)建是朝著可重現(xiàn)構(gòu)建邁出的一大步,我們也堅(jiān)信這一點(diǎn)。.NET SDK 和 C# 編譯器具有重要的可重現(xiàn)構(gòu)建功能。

庫 API

除了已經(jīng)涵蓋的 API 之外,還添加了以下 API。

WebSocket 壓縮

壓縮對(duì)于通過網(wǎng)絡(luò)傳輸?shù)娜魏螖?shù)據(jù)都很重要。WebSockets 現(xiàn)在啟用壓縮。我們使用了WebSockets的擴(kuò)展實(shí)現(xiàn),RFC 7692。它允許使用該算法壓縮 WebSockets 消息有效負(fù)載。此功能是用戶對(duì) GitHub 上 Networking 的最高要求之一。permessage-deflateDEFLATE

與加密一起使用的壓縮可能會(huì)導(dǎo)致攻擊,例如CRIMEBREACH。這意味著不能在單個(gè)壓縮上下文中將秘密與用戶生成的數(shù)據(jù)一起發(fā)送,否則可以提取該秘密。為了讓用戶注意這些影響并幫助他們權(quán)衡風(fēng)險(xiǎn),我們將其中一個(gè)關(guān)鍵 API 命名為DangerousDeflateOptions。我們還添加了對(duì)特定消息關(guān)閉壓縮的功能,因此如果用戶想要發(fā)送機(jī)密,他們可以在不壓縮的情況下安全地發(fā)送。

禁用壓縮時(shí) WebSocket內(nèi)存占用減少了約 27%。

從客戶端啟用壓縮很容易,如下例所示。但是,請(qǐng)記住,服務(wù)器可以協(xié)商設(shè)置,例如請(qǐng)求較小的窗口或完全拒絕壓縮。

var cws = new ClientWebSocket();
cws.Options.DangerousDeflateOptions = new WebSocketDeflateOptions()
{
    ClientMaxWindowBits = 10,
    ServerMaxWindowBits = 10
};

還添加了對(duì) ASP.NET Core 的 WebSocket 壓縮支持。

感謝伊萬茲拉塔諾夫

襪子代理支持

SOCKS是一種代理服務(wù)器實(shí)現(xiàn),可以處理任何 TCP 或 UDP 流量,使其成為一個(gè)非常通用的系統(tǒng)。這是一個(gè)長期存在的社區(qū)請(qǐng)求,已添加到 .NET 6 中

此更改增加了對(duì) Socks4、Socks4a 和 Socks5 的支持。例如,它允許通過 SSH 測試外部連接或連接到 Tor 網(wǎng)絡(luò)。

該WebProxy班現(xiàn)在接受socks的方案,你可以在下面的例子中看到。

var handler = new HttpClientHandler
{
    Proxy = new WebProxy("socks5://127.0.0.1", 9050)
};
var httpClient = new HttpClient(handler);

感謝霍耀元。

Microsoft.Extensions.Hosting — ConfigureHostOptions API

我們?cè)?IHostBuilder 上添加了一個(gè)新的 ConfigureHostOptions API 以簡化應(yīng)用程序設(shè)置(例如,配置關(guān)閉超時(shí)):

using HostBuilder host = new()
    .ConfigureHostOptions(o =>
    {
        o.ShutdownTimeout = TimeSpan.FromMinutes(10);
    })
    .Build();

host.Run();

在 .NET 5 中,配置主機(jī)選項(xiàng)有點(diǎn)復(fù)雜:

using HostBuilder host = new()
    .ConfigureServices(services =>
    {
        services.Configure(o =>
        {
            o.ShutdownTimeout = TimeSpan.FromMinutes(10);
        });
    })
    .Build();

host.Run();

Microsoft.Extensions.DependencyInjection — CreateAsyncScope API

CreateAsyncScopeAPI是為了處理處置IAsyncDisposable服務(wù)。以前,您可能已經(jīng)注意到,對(duì)IAsyncDisposable服務(wù)提供者的處置可能會(huì)引發(fā)InvalidOperationException異常。

下面的示例演示了新模式,CreateAsyncScope用于啟用using語句的安全使用。

await using (var scope = provider.CreateAsyncScope())
{
    var foo = scope.ServiceProvider.GetRequiredService();
}

下面的例子演示了現(xiàn)有的問題案例:

using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;

await using var provider = new ServiceCollection()
        .AddScoped()
        .BuildServiceProvider();

// This using can throw InvalidOperationException
using (var scope = provider.CreateScope())
{
    var foo = scope.ServiceProvider.GetRequiredService();
}

class Foo : IAsyncDisposable
{
    public ValueTask DisposeAsync() => default;
}

以下模式是之前建議的避免異常的解決方法。不再需要它。

var scope = provider.CreateScope();
var foo = scope.ServiceProvider.GetRequiredService();
await ((IAsyncDisposable)scope).DisposeAsync();

感謝馬丁Bj?rkstr?m

Microsoft.Extensions.Logging — 編譯時(shí)源代碼生成器

.NET 6href="https://github.com/dotnet/runtime/issues/52549">引入了LoggerMessageAttribute類型. 此屬性是命名空間的一部分,使用時(shí),它會(huì)源生成高性能日志 API。源代碼生成日志支持旨在為現(xiàn)代 .NET 應(yīng)用程序提供高度可用和高性能的日志解決方案。自動(dòng)生成的源代碼依賴于接口和功能。Microsoft.Extensions.LoggingILoggerLoggerMessage.Define

當(dāng)LoggerMessageAttribute用于partial日志記錄方法時(shí)觸發(fā)源生成器。當(dāng)被觸發(fā)時(shí),它要么能夠自動(dòng)生成partial它正在裝飾的方法的實(shí)現(xiàn),要么生成帶有正確使用提示的編譯時(shí)診斷。編譯時(shí)日志記錄解決方案在運(yùn)行時(shí)通常比現(xiàn)有日志記錄方法快得多。它通過最大限度地消除裝箱、臨時(shí)分配和副本來實(shí)現(xiàn)這一點(diǎn)。

與直接手動(dòng)使用API 相比,有以下好處:LoggerMessage.Define

  • 更短更簡單的語法:聲明性屬性使用而不是編碼樣板。

  • 引導(dǎo)開發(fā)者體驗(yàn):生成器給出警告,幫助開發(fā)者做正確的事。

  • 支持任意數(shù)量的日志參數(shù)。最多支持六個(gè)。LoggerMessage.Define

  • 支持動(dòng)態(tài)日志級(jí)別。這是不可能的。LoggerMessage.Define

要使用LoggerMessageAttribute,消費(fèi)類和方法需要是partial。代碼生成器在編譯時(shí)觸發(fā)并生成該partial方法的實(shí)現(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并且在屬性定義中指定了日志級(jí)別。在靜態(tài)上下文中使用屬性時(shí),ILogger需要實(shí)例作為參數(shù)。您也可以選擇在非靜態(tài)上下文中使用該屬性。有關(guān)更多示例和使用場景,請(qǐng)?jiān)L問編譯時(shí)日志源生成器文檔。

System.Linq — 可枚舉支持Index和Range參數(shù)

該方法現(xiàn)在接受可枚舉項(xiàng)末尾的索引,如下例所示。Enumerable.ElementAt

Enumerable.Range(1, 10).ElementAt(^2); // returns 9

添加了一個(gè)接受參數(shù)的重載。它簡化了對(duì)可枚舉序列的切片:Enumerable.TakeRange

  • 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方法嘗試在不強(qiáng)制枚舉的情況下獲取源可枚舉的計(jì)數(shù)。這種方法在枚舉之前預(yù)分配緩沖區(qū)很有用的情況下很有用,如下例所示。

List buffer = source.TryGetNonEnumeratedCount(out int count) ? new List(capacity: count) : new List();
foreach (T item in source)
{
    buffer.Add(item);
}

TryGetNonEnumeratedCount檢查實(shí)現(xiàn)ICollection/或利用Linq 使用的一些內(nèi)部優(yōu)化的源。ICollection

System.Linq — DistinctBy/ UnionBy/ IntersectBy/ExceptBy

新的變體已添加到集合操作中,允許使用鍵選擇器函數(shù)指定相等性,如下面的示例所示。

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 chunks = Enumerable.Range(0, 10).Chunk(size: 3); // { {0,1,2}, {3,4,5}, {6,7,8}, {9} }

歸功于羅伯特·安德森

System.Linq的- FirstOrDefault/ LastOrDefault/SingleOrDefault過載采取默認(rèn)參數(shù)

現(xiàn)有FirstOrDefault/ LastOrDefault/SingleOrDefault方法返回如果源枚舉是空的。添加了新的重載,接受在這種情況下要返回的默認(rèn)參數(shù),如下面的示例所示。default(T)

Enumerable.Empty().SingleOrDefault(-1); // returns -1

感謝@ Foxtrek64

System.Linq —Zip接受三個(gè)枚舉的重載

郵編方法現(xiàn)在支持組合三個(gè)枚舉接口,你可以在下面的例子中看到。

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))
{
}

感謝霍耀元。

優(yōu)先隊(duì)列

PriorityQueue(System.Collections.Generic) 是一個(gè)新集合,可以添加具有值和優(yōu)先級(jí)的新項(xiàng)目。在出隊(duì)時(shí),PriorityQueue 返回具有最低優(yōu)先級(jí)值的元素。您可以將這個(gè)新集合視為類似于但每個(gè)入隊(duì)元素都有一個(gè)影響出隊(duì)行為的優(yōu)先級(jí)值。Queue

以下示例演示了.PriorityQueue

// creates a priority queue of strings with integer priorities
var pq = new PriorityQueue();

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

更快地將結(jié)構(gòu)體作為字典值處理

CollectionsMarshal.GetValueRef是一個(gè)新的不安全API,它可以更快地更新字典中的結(jié)構(gòu)值。新 API 旨在用于高性能場景,而不是用于一般用途。它返回refstruct 值,然后可以使用典型技術(shù)就地更新。

以下示例演示了如何使用新 API:

ref MyStruct value = CollectionsMarshal.GetValueRef(dictionary, key);
// Returns Unsafe.NullRef() if it doesn't exist; check using Unsafe.IsNullRef(ref value)
if (!Unsafe.IsNullRef(ref value))
{
    // Mutate in-place
    value.MyInt++;
}

在此更改之前,更新struct字典值對(duì)于高性能場景可能會(huì)很昂貴,需要字典查找和struct. 然后在更改 之后struct,它將再次分配給字典鍵,從而導(dǎo)致另一次查找和復(fù)制操作。此改進(jìn)將密鑰散列減少到 1(從 2)并刪除所有結(jié)構(gòu)復(fù)制操作。

歸功于本·亞當(dāng)斯。

新的DateOnly和TimeOnly結(jié)構(gòu)

添加了僅限日期和時(shí)間的結(jié)構(gòu),具有以下特征:

  • 每個(gè)代表 a 的一半DateTime,或者只是日期部分,或者只是時(shí)間部分。

  • DateOnly是生日、周年紀(jì)念日和工作日的理想選擇。它符合 SQL Server 的date類型。

  • TimeOnly是定期會(huì)議、鬧鐘和每周工作時(shí)間的理想選擇。它符合 SQL Server 的time類型。

  • 補(bǔ)充現(xiàn)有的日期/時(shí)間類型 ( DateTime, DateTimeOffset, TimeSpan, TimeZoneInfo)。

  • 在System命名空間中,在 CoreLib 中提供,就像現(xiàn)有的相關(guān)類型一樣。

性能改進(jìn) DateTime.UtcNow

這種改進(jìn)有以下好處:

  • 修復(fù)了在 Windows 上獲取系統(tǒng)時(shí)間的2.5 倍性能回歸

  • 利用 5 分鐘的 Windows 閏秒數(shù)據(jù)滑動(dòng)緩存,而不是在每次調(diào)用時(shí)獲取。

支持所有平臺(tái)上的 Windows 和 IANA 時(shí)區(qū)

這種改進(jìn)有以下好處:

  • 使用時(shí)的隱式轉(zhuǎn)換(https://github.com/dotnet/runtime/pull/49412)TimeZoneInfo.FindSystemTimeZoneById

  • 通過新的API顯式轉(zhuǎn)換上TimeZoneInfo:TryConvertIanaIdToWindowsId,TryConvertWindowsIdToIanaId,和HasIanaId(https://github.com/dotnet/runtime/issues/49407

  • 改進(jìn)了使用不同時(shí)區(qū)類型的系統(tǒng)之間的跨平臺(tái)支持和互操作。

  • 刪除需要使用 TimeZoneConverter OSS 庫。該功能現(xiàn)已內(nèi)置。

改進(jìn)的時(shí)區(qū)顯示名稱

Unix 上的時(shí)區(qū)顯示名稱已得到改進(jìn)

  • 從 返回的列表中的顯示名稱中消除歧義。TimeZoneInfo.GetSystemTimeZones

  • 利用 ICU / CLDR 全球化數(shù)據(jù)。

  • 僅適用于 Unix。Windows 仍然使用注冊(cè)表數(shù)據(jù)。這可能會(huì)在以后更改。

還進(jìn)行了以下額外改進(jìn):

  • UTC 時(shí)區(qū)的顯示名稱和標(biāo)準(zhǔn)名稱被硬編碼為英語,現(xiàn)在使用與其余時(shí)區(qū)數(shù)據(jù)相同的語言(CurrentUICulture在 Unix 上,Windows 上的操作系統(tǒng)默認(rèn)語言)。

  • 由于大小限制,Wasm 中的時(shí)區(qū)顯示名稱改為使用非本地化的 IANA ID。

  • TimeZoneInfo.AdjustmentRule嵌套類將其BaseUtcOffsetDelta內(nèi)部屬性公開并獲得一個(gè)新的構(gòu)造函數(shù)baseUtcOffsetDelta作為參數(shù)。(https://github.com/dotnet/runtime/issues/50256

  • TimeZoneInfo.AdjustmentRule 還獲得了在 Unix 上加載時(shí)區(qū)的其他修復(fù)(https://github.com/dotnet/runtime/pull/49733),(https://github.com/dotnet/runtime/pull/50131

改進(jìn)了對(duì) Windows ACL 的支持

System.Threading.AccessControl現(xiàn)在包括對(duì)與 Windows 訪問控制列表 (ACL) 交互的改進(jìn)支持。為、和的OpenExisting和TryOpenExisting方法添加了新的重載。這些具有“安全權(quán)限”實(shí)例的重載允許打開使用特殊 Windows 安全屬性創(chuàng)建的線程同步對(duì)象的現(xiàn)有實(shí)例。EventWaitHandleMutexSemaphore

此更新與 .NET Framework 中可用的 API 相匹配,并且具有相同的行為。

以下示例演示如何使用這些新 API。

對(duì)于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 一次性方法

HMAC類現(xiàn)在有靜態(tài)方法,允許HMACs的一次性計(jì)算不分配。這些添加類似于先前版本中添加的用于哈希生成的一次性方法。System.Security.Cryptography

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)建高級(jí)系統(tǒng),例如復(fù)雜的緩存系統(tǒng)或該類型的自定義版本。例如,MVVM 工具包中的類型將使用它來避免廣播消息時(shí)的內(nèi)存分配。ConditionalWeakTableWeakReferenceMessenger

可移植線程池

.NET線程池已重新實(shí)現(xiàn)作為一個(gè)托管實(shí)現(xiàn),現(xiàn)在作為默認(rèn)的線程池.NET 6.我們做出這一改變,使所有的.NET應(yīng)用程序能夠訪問線程池獨(dú)立的是否相同正在使用 CoreCLR、Mono 或任何其他運(yùn)行時(shí)。作為此更改的一部分,我們沒有觀察到或預(yù)期任何功能或性能影響。

RyuJIT

該團(tuán)隊(duì)對(duì)該版本的 .NET JIT 編譯器進(jìn)行了許多改進(jìn),在每個(gè)預(yù)覽帖子中都有記錄。大多數(shù)更改都可以提高性能。這里涵蓋了一些 RyuJIT 的亮點(diǎn)。

動(dòng)態(tài) PGO

在 .NET 6 中,我們啟用了兩種形式的 PGO(配置文件引導(dǎo)優(yōu)化):

  • 動(dòng)態(tài) PGO使用從當(dāng)前運(yùn)行收集的數(shù)據(jù)來優(yōu)化當(dāng)前運(yùn)行。

  • 靜態(tài) PGO依靠從過去運(yùn)行中收集的數(shù)據(jù)來優(yōu)化未來運(yùn)行。

動(dòng)態(tài) PGO 已經(jīng)在文章前面的性能部分中介紹過。我會(huì)提供一個(gè)重新上限。

動(dòng)態(tài) PGO 使 JIT 能夠在運(yùn)行時(shí)收集有關(guān)實(shí)際用于該特定應(yīng)用程序運(yùn)行的代碼路徑和類型的信息。然后 JIT 可以根據(jù)這些代碼路徑優(yōu)化代碼,有時(shí)會(huì)顯著提高性能。我們?cè)跍y試和生產(chǎn)方面都看到了健康的兩位數(shù)改進(jìn)。有一組經(jīng)典的編譯器技術(shù),在沒有 PGO 的情況下使用 JIT 或提前編譯是不可能的。我們現(xiàn)在能夠應(yīng)用這些技術(shù)。熱/冷分裂是一種這樣的技術(shù),去虛擬化是另一種技術(shù)。

要啟用動(dòng)態(tài) PGO,請(qǐng)?jiān)谀膽?yīng)用程序?qū)⑦\(yùn)行的環(huán)境中進(jìn)行設(shè)置。DOTNET_TieredPGO=1

如性能部分所述,動(dòng)態(tài) PGO 為 TechEmpower JSON“MVC”套件的每秒請(qǐng)求數(shù)提供了 26% 的改進(jìn)(510K -> 640K)。這是一個(gè)驚人的改進(jìn),無需更改代碼。

我們的目標(biāo)是在 .NET 的未來版本中默認(rèn)啟用 Dynamic PGO,希望與 .NET 7 一起使用。我們強(qiáng)烈建議您在您的應(yīng)用程序中嘗試使用 Dynamic PGO 并向我們提供反饋。

完整的 PGO

要獲得動(dòng)態(tài) PGO 的全部優(yōu)勢,您可以設(shè)置兩個(gè)額外的環(huán)境變量:和. 這確保了盡可能多的方法參與分層編譯。我們稱這種變體為Full PGO。與動(dòng)態(tài) PGO 相比,完整 PGO 可以提供更大的穩(wěn)態(tài)性能優(yōu)勢,但啟動(dòng)時(shí)間會(huì)更慢(因?yàn)楸仨氃诘?0 層執(zhí)行更多方法)。DOTNET_TC_QuickJitForLoops=1DOTNET_ReadyToRun=0

您不希望將此選項(xiàng)用于短期運(yùn)行的無服務(wù)器應(yīng)用程序,但對(duì)于長期運(yùn)行的應(yīng)用程序可能有意義。

在未來的版本中,我們計(jì)劃精簡和簡化這些選項(xiàng),以便您可以更簡單地獲得完整 PGO 的好處,并適用于更廣泛的應(yīng)用程序。

靜態(tài) PGO

我們目前使用靜態(tài) PGO來優(yōu)化 .NET 庫程序集,如隨 R2R(準(zhǔn)備運(yùn)行)提供的程序集。System.Private.CoreLib

靜態(tài) PGO 的好處在于,當(dāng)程序集使用 crossgen 編譯為 R2R 格式時(shí),會(huì)進(jìn)行優(yōu)化。這意味著在沒有運(yùn)行時(shí)成本的情況下有運(yùn)行時(shí)優(yōu)勢。例如,這非常重要,這也是 PGO 對(duì) C++ 很重要的原因。

循環(huán)對(duì)齊

內(nèi)存對(duì)齊是現(xiàn)代計(jì)算中各種操作的常見要求。在 .NET 5 中,我們開始在 32 字節(jié)邊界對(duì)齊方法。在 .NET 6 中,我們添加了一個(gè)功能來執(zhí)行自適應(yīng)循環(huán)對(duì)齊,該功能NOP在具有循環(huán)的方法中添加填充指令,以便循環(huán)代碼從 mod(16) 或 mod(32) 內(nèi)存地址開始。這些更改提高并穩(wěn)定了 .NET 代碼的性能。

在下面的冒泡排序圖中,數(shù)據(jù)點(diǎn) 1 表示我們開始在 32 字節(jié)邊界對(duì)齊方法的點(diǎn)。數(shù)據(jù)點(diǎn) 2 表示我們也開始對(duì)齊內(nèi)部循環(huán)的點(diǎn)。如您所見,基準(zhǔn)測試的性能和穩(wěn)定性都有顯著提高。





硬件加速結(jié)構(gòu)

結(jié)構(gòu)是 CLR 類型系統(tǒng)的重要組成部分。近年來,它們經(jīng)常被用作整個(gè) .NET 庫中的性能原語。最近的例子是ValueTask,ValueTuple和。記錄結(jié)構(gòu)是一個(gè)新的例子。在 .NET 5 和 .NET 6 中,我們一直在提高結(jié)構(gòu)的性能,部分是通過確保結(jié)構(gòu)可以保存在超快的 CPU 寄存器中,當(dāng)它們是局部變量、參數(shù)或方法的返回值時(shí))。這對(duì)于使用向量計(jì)算的 API 尤其有益。Span

穩(wěn)定性能測量

團(tuán)隊(duì)中有大量的工程系統(tǒng)工作從未出現(xiàn)在博客上。這適用于您使用的任何硬件或軟件產(chǎn)品。JIT 團(tuán)隊(duì)開展了一個(gè)項(xiàng)目來穩(wěn)定性能測量,目標(biāo)是增加我們內(nèi)部性能實(shí)驗(yàn)室自動(dòng)化自動(dòng)報(bào)告的回歸值。這個(gè)項(xiàng)目很有趣,因?yàn)檫M(jìn)行了深入的調(diào)查以及實(shí)現(xiàn)穩(wěn)定性所需的產(chǎn)品更改。它還展示了我們衡量保持和提高績效的規(guī)模。





此圖像展示了不穩(wěn)定的性能測量,其中性能在連續(xù)運(yùn)行中在慢速和快速之間波動(dòng)。x 軸是測試日期,y 軸是測試時(shí)間(以納秒為單位)。在圖表的末尾(在提交這些更改之后),您可以看到測量值穩(wěn)定下來,結(jié)果最好。此圖像演示了單個(gè)測試。還有更多測試在dotnet/runtime #43227中被證明具有類似的行為。

準(zhǔn)備運(yùn)行的代碼 / Crossgen 2

Crossgen2 是crossgen 工具的替代品。它旨在滿足兩個(gè)結(jié)果:

  • 使跨代開發(fā)更高效。

  • 啟用一組當(dāng)前無法通過 crossgen 實(shí)現(xiàn)的功能。

這種轉(zhuǎn)換有點(diǎn)類似于本機(jī)代碼 csc.exe 到托管代碼Roslyn 編譯器。Crossgen2 是用 C# 編寫的,但是,它沒有像 Roslyn 那樣公開花哨的 API。

我們可能已經(jīng)/已經(jīng)為 .NET 6 和 7 計(jì)劃了六個(gè)依賴于 crossgen2 的項(xiàng)目。矢量指令默認(rèn)值提議是我們想要為 .NET 6 進(jìn)行的 crossgen2 功能和產(chǎn)品更改的一個(gè)很好的例子,但更有可能是 .NET 7。版本氣泡是另一個(gè)很好的例子。

Crossgen2 支持跨操作系統(tǒng)和架構(gòu)維度的交叉編譯(因此得名“crossgen”)。這意味著您將能夠使用單個(gè)構(gòu)建機(jī)器為所有目標(biāo)生成本機(jī)代碼,至少與準(zhǔn)備運(yùn)行的代碼相關(guān)。然而,運(yùn)行和測試該代碼是另一回事,為此您需要適當(dāng)?shù)挠布筒僮飨到y(tǒng)。

第一步是用crossgen2編譯平臺(tái)本身。我們使用 .NET 6 完成了所有架構(gòu)。因此,我們能夠在此版本中淘汰舊的 crossgen。請(qǐng)注意,crossgen2 僅適用于 CoreCLR,不適用于基于 Mono 的應(yīng)用程序(它們具有一組單獨(dú)的代碼生成工具)。

這個(gè)項(xiàng)目——至少在開始時(shí)——并不以性能為導(dǎo)向。目標(biāo)是為托管 RyuJIT(或任何其他)編譯器提供更好的架構(gòu),以離線方式(不需要或啟動(dòng)運(yùn)行時(shí))生成代碼。

您可能會(huì)說“嘿……如果 crossgen2 是用 C# 編寫的,那么您不必啟動(dòng)運(yùn)行時(shí)來運(yùn)行它嗎?” 是的,但這不是本文中“離線”的意思。當(dāng) crossgen2 運(yùn)行時(shí),我們沒有使用 crossgen2 運(yùn)行時(shí)附帶的 JIT 來生成準(zhǔn)備運(yùn)行 (R2R) 代碼. 那是行不通的,至少對(duì)于我們的目標(biāo)是行不通的。想象 crossgen2 在 x64 機(jī)器上運(yùn)行,我們需要為 Arm64 生成代碼。Crossgen2 加載 Arm64 RyuJIT(為 x64 編譯)作為本機(jī)插件,然后使用它生成 Arm64 R2R 代碼。機(jī)器指令只是保存到文件中的字節(jié)流。它也可以反方向工作。在 Arm64 上,crossgen2 可以使用編譯為 Arm64 的 x64 RyuJIT 生成 x64 代碼。我們使用相同的方法在 x64 機(jī)器上定位 x64 代碼。Crossgen2 加載為任何需要的配置構(gòu)建的 RyuJIT。這可能看起來很復(fù)雜,但如果您想啟用無縫的交叉目標(biāo)模型,它就是您需要的那種系統(tǒng),而這正是我們想要的。

我們希望只在一個(gè)版本中使用“crossgen2”這個(gè)術(shù)語,之后它將取代現(xiàn)有的 crossgen,然后我們將回到對(duì)“crossgen2”使用“crossgen”這個(gè)術(shù)語。

.NET 診斷:EventPipe

EventPipe 是我們的跨平臺(tái)機(jī)制,用于在進(jìn)程內(nèi)或進(jìn)程外輸出事件、性能數(shù)據(jù)和計(jì)數(shù)器。從 .NET 6 開始,我們已將實(shí)現(xiàn)從 C++ 移至 C。通過此更改,Mono 也使用 EventPipe。這意味著 CoreCLR 和 Mono 使用相同的事件基礎(chǔ)結(jié)構(gòu),包括 .NET 診斷 CLI 工具。

這一變化還伴隨著 CoreCLR 的小幅縮小:


我們還進(jìn)行了一些更改,以在負(fù)載下提高 EventPipe 吞吐量。在最初的幾個(gè)預(yù)覽版中,我們進(jìn)行了一系列更改,使吞吐量提高了 .NET 5 所能達(dá)到的 2.06 倍:





對(duì)于此基準(zhǔn)測試,越高越好。.NET 6 是橙色線,.NET 5 是藍(lán)色線。

開發(fā)工具包

對(duì) .NET SDK 進(jìn)行了以下改進(jìn)。

.NET 6 SDK 可選工作負(fù)載的 CLI 安裝

.NET 6 引入了SDK 工作負(fù)載的概念。工作負(fù)載是可選組件,可以安裝在 .NET SDK 之上以啟用各種方案。.NET 6 中的新工作負(fù)載是:.NET MAUI 和 Blazor WebAssembly AOT 工作負(fù)載。我們可能會(huì)在 .NET 7 中創(chuàng)建新的工作負(fù)載(可能來自現(xiàn)有的 SDK)。工作負(fù)載的最大好處是尺寸減小和可選性。我們希望隨著時(shí)間的推移使 SDK 變得更小,并允許只安裝您需要的組件。這個(gè)模型對(duì)開發(fā)者機(jī)器有好處,甚至對(duì) CI 更好。

Visual Studio 用戶實(shí)際上不需要擔(dān)心工作負(fù)載。工作負(fù)載功能的設(shè)計(jì)目的是讓安裝協(xié)調(diào)器(如 Visual Studio)可以為您安裝工作負(fù)載。可以通過 CLI 直接管理工作負(fù)載。

工作負(fù)載功能公開了多個(gè)用于管理工作負(fù)載的動(dòng)詞,包括以下內(nèi)容:

  • dotnet workload restore — 安裝給定項(xiàng)目所需的工作負(fù)載。

  • dotnet workload install — 安裝命名的工作負(fù)載。

  • dotnet workload list — 列出您已安裝的工作負(fù)載。

  • dotnet workload update — 將所有已安裝的工作負(fù)載更新到最新的可用版本。

該update動(dòng)詞查詢更新的工作負(fù)載清單、更新本地清單、下載已安裝工作負(fù)載的新版本,然后刪除工作負(fù)載的所有舊版本。這類似于(在基于 Debian 的 Linux 發(fā)行版上使用)。將工作負(fù)載視為 SDK 的私有包管理器是合理的。它是私有的,因?yàn)樗鼉H可用于 SDK 組件。我們將來可能會(huì)重新考慮這一點(diǎn)。nuget.orgapt update && apt upgrade -y

這些dotnet workload命令在給定 SDK 的上下文中運(yùn)行。假設(shè)您同時(shí)安裝了 .NET 6 和 .NET 7。工作負(fù)載命令將為每個(gè) SDK 提供不同的結(jié)果,因?yàn)楣ぷ髫?fù)載會(huì)有所不同(至少相同工作負(fù)載的不同版本)。

請(qǐng)注意,dotnet workload install將工作負(fù)載從 NuGet.org 復(fù)制到您的 SDK 安裝中,因此sudo如果 SDK 安裝位置受到保護(hù)(意味著在管理員/根位置),則需要提升運(yùn)行或使用。

內(nèi)置SDK版本檢查

為了更輕松地跟蹤新版本的 SDK 和運(yùn)行時(shí)何時(shí)可用,我們向 .NET 6 SDK 添加了一個(gè)新命令。

dotnet sdk check

它會(huì)告訴您是否有更新版本可用于您已安裝的任何 .NET SDK、運(yùn)行時(shí)或工作負(fù)載。您可以在下圖中看到新體驗(yàn)。





dotnet new

您現(xiàn)在可以在http://NuGet.org中搜索帶有.dotnet new --search

模板安裝的其他改進(jìn)包括支持開關(guān)以支持私有 NuGet 源的授權(quán)憑據(jù)。--interactive

安裝 CLI 模板后,您可以通過和檢查更新是否可用。--update-check--update-apply

NuGet 包驗(yàn)證

包驗(yàn)證工具使 NuGet 庫開發(fā)人員能夠驗(yàn)證他們的包是否一致且格式良好。

這包括:

  • 驗(yàn)證跨版本沒有重大更改。

  • 驗(yàn)證包對(duì)于所有特定于運(yùn)行時(shí)的實(shí)現(xiàn)是否具有相同的公共 API 集。

  • 確定任何目標(biāo)框架或運(yùn)行時(shí)適用性差距。

此工具是 SDK 的一部分。使用它的最簡單方法是在項(xiàng)目文件中設(shè)置一個(gè)新屬性。

 true

更多 Roslyn 分析儀

在 .NET 5 中,我們隨 .NET SDK 提供了大約 250 個(gè)分析器。其中許多已經(jīng)存在,但作為 NuGet 包在帶外發(fā)布。我們為 .NET 6 添加了更多分析器。

默認(rèn)情況下,大多數(shù)新分析器在信息級(jí)別啟用。您可以通過啟用這些分析儀在警告級(jí)別配置的分析模式是這樣的:。All

我們發(fā)布了我們想要的 .NET 6 分析器集(加上一些額外的東西),然后將其中的大部分都準(zhǔn)備好了。社區(qū)添加了幾個(gè)實(shí)現(xiàn),包括這些。

感謝Meik TranelNewell Clark。

為平臺(tái)兼容性分析器啟用自定義防護(hù)

該CA1416平臺(tái)兼容性分析儀已經(jīng)可以識(shí)別使用的方法平臺(tái)警衛(wèi)OperatingSystem和RuntimeInformation,如和。但是,分析器不識(shí)別任何其他保護(hù)可能性,例如緩存在字段或?qū)傩灾械钠脚_(tái)檢查結(jié)果,或者在輔助方法中定義了復(fù)雜的平臺(tái)檢查邏輯。OperatingSystem.IsWindowsOperatingSystem.IsWindowsVersionAtLeast

為了允許自定義保護(hù)的可能性,我們添加了新屬性SupportedOSPlatformGuard并UnsupportedOSPlatformGuard使用相應(yīng)的平臺(tái)名稱和/或版本注釋自定義保護(hù)成員。該注釋被平臺(tái)兼容性分析器的流分析邏輯識(shí)別和尊重。

用法

    [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。它是另一個(gè)巨大的 .NET 版本,在性能、功能、可用性和安全性方面的改進(jìn)幾乎相同。我們希望您找到許多改進(jìn),最終使您在日常開發(fā)中更有效率和能力,并提高性能或降低生產(chǎn)應(yīng)用程序的成本。我們已經(jīng)開始聽到你們中那些已經(jīng)開始使用 .NET 6 的人的好消息。

在 Microsoft,我們也處于 .NET 6 部署的早期階段,一些關(guān)鍵應(yīng)用程序已經(jīng)投入生產(chǎn),未來幾周和幾個(gè)月內(nèi)還有更多應(yīng)用程序即將推出。

.NET 6 是我們最新的 LTS 版本。我們鼓勵(lì)所有人轉(zhuǎn)向它,特別是如果您使用 .NET 5。我們期待它成為有史以來采用速度最快的 .NET 版本。

此版本是至少 1000 人(但可能更多)的結(jié)果。這包括來自 Microsoft 的 .NET 團(tuán)隊(duì)以及社區(qū)中的更多人。我試圖在這篇文章中包含許多社區(qū)貢獻(xiàn)的功能。感謝您花時(shí)間創(chuàng)建這些并完成我們的流程。我希望這次經(jīng)歷是好的,更多的人會(huì)做出貢獻(xiàn)。

這篇文章是許多有才華的人合作的結(jié)果。這些貢獻(xiàn)包括團(tuán)隊(duì)在整個(gè)發(fā)布過程中提供的功能內(nèi)容、為此最終帖子創(chuàng)建的重要新內(nèi)容,以及使最終內(nèi)容達(dá)到您應(yīng)得的質(zhì)量所需的大量技術(shù)和散文更正。很高興為您制作它和所有其他帖子。

感謝您成為 .NET 開發(fā)人員。


來源:知乎