.NET 6 攻略大全(三)
點(diǎn)擊上方藍(lán)字
關(guān)注我們
(本文閱讀時(shí)間:15分鐘)
.NET 6 繼續(xù)與大家相約周日啦。本篇文章將介紹:單文件應(yīng)用、IL 修整、System.Text.Json、源代碼構(gòu)建、庫(kù)AIP的相關(guān)攻略。?
單文件應(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)建塊。“apphost” 是在非單文件情況下啟動(dòng)應(yīng)用程序的可執(zhí)行文件,例如 myapp.exe或./myapp. Apphost 包含用于查找運(yùn)行時(shí)、加載它并使用該運(yùn)行時(shí)啟動(dòng)您的應(yīng)用程序的代碼。Superhost 仍然執(zhí)行其中一些任務(wù),但使用所有 CoreCLR 本機(jī)二進(jìn)制文件的靜態(tài)鏈接副本。靜態(tài)鏈接是我們用來(lái)實(shí)現(xiàn)單一文件體驗(yàn)的方法。本機(jī)依賴項(xiàng)(如 NuGet 包附帶的)是單文件嵌入的顯著例外。默認(rèn)情況下,它們不包含在單個(gè)文件中。例如,WPF 本機(jī)依賴項(xiàng)不是超級(jí)主機(jī)的一部分,因此會(huì)在單文件應(yīng)用程序之外產(chǎn)生其他文件。您可以使用該設(shè)置 IncludeNativeLibrariesForSelfExtract 嵌入和提取本機(jī)依賴項(xiàng)。
▌靜態(tài)分析
我們改進(jìn)了單文件分析器以允許自定義警告。如果您的 API 在單文件發(fā)布中不起作用,您現(xiàn)在可以使用[RequiresAssemblyFiles]屬性對(duì)其進(jìn)行標(biāo)記,如果啟用了分析器,則會(huì)出現(xiàn)警告。添加該屬性還將使方法中與單個(gè)文件相關(guān)的所有警告靜音,因此您可以使用該警告將警告向上傳播到您的公共 API。
當(dāng) PublishSingleFile 設(shè)置為 true 時(shí),會(huì)自動(dòng)為 exe 項(xiàng)目啟用單文件分析器,但您也可以通過(guò)將 EnableSingleFileAnalysis 設(shè)置為 true 來(lái)為任何項(xiàng)目啟用它。如果您想支持將庫(kù)作為單個(gè)文件應(yīng)用程序的一部分,這將很有幫助。
在 .NET 5 中,我們?yōu)閱挝募行袨椴煌?Assembly.Location 和一些其他 API 添加了警告。
▌壓縮
單文件包現(xiàn)在支持壓縮,可以通過(guò)將屬性設(shè)置 EnableCompressionInSingleFile為true. 在運(yùn)行時(shí),文件會(huì)根據(jù)需要解壓縮到內(nèi)存中。壓縮可以為某些場(chǎng)景節(jié)省大量空間。
讓我們看一下與 NuGet 包資源管理器一起使用的單個(gè)文件發(fā)布(帶壓縮和不帶壓縮)。
無(wú)壓縮:172 MB
壓縮:71.6 MB
壓縮會(huì)顯著增加應(yīng)用程序的啟動(dòng)時(shí)間,尤其是在 Unix 平臺(tái)上。Unix 平臺(tái)有一個(gè)不能用于壓縮的無(wú)拷貝快速啟動(dòng)路徑。您應(yīng)該在啟用壓縮后測(cè)試您的應(yīng)用程序,看看額外的啟動(dòng)成本是否可以接受。
▌單文件調(diào)試
目前只能使用平臺(tái)調(diào)試器(如 WinDBG)來(lái)調(diào)試單文件應(yīng)用程序。我們正在考慮使用更高版本的 Visual Studio 2022 添加 Visual Studio 調(diào)試。
▌macOS 上的單文件簽名
單文件應(yīng)用程序現(xiàn)在滿足 macOS 上的 Apple 公證和簽名要求。具體更改與我們根據(jù)離散文件布局構(gòu)建單文件應(yīng)用程序的方式有關(guān)。
Apple 開(kāi)始對(duì)macOS Catalina實(shí)施新的簽名和公證要求。我們一直在與 Apple 密切合作,以了解需求,并尋找使 .NET 等開(kāi)發(fā)平臺(tái)能夠在該環(huán)境中正常工作的解決方案。我們已經(jīng)進(jìn)行了產(chǎn)品更改并記錄了用戶工作流程,以滿足 Apple 在最近幾個(gè) .NET 版本中的要求。剩下的差距之一是單文件簽名,這是在 macOS 上分發(fā) .NET 應(yīng)用程序的要求,包括在 macOS 商店中。
IL 修整
該團(tuán)隊(duì)一直致力于為多個(gè)版本進(jìn)行 IL 修整。.NET 6 代表了這一旅程向前邁出的重要一步。我們一直在努力使更激進(jìn)的修剪模式安全且可預(yù)測(cè),因此有信心將其設(shè)為默認(rèn)模式。TrimMode=link 以前是可選功能,現(xiàn)在是默認(rèn)功能。
我們有一個(gè)三管齊下的修剪策略:
提高平臺(tái)的修剪能力。
對(duì)平臺(tái)進(jìn)行注釋以提供更好的警告并使其他人也能這樣做。
在此基礎(chǔ)上,讓默認(rèn)的修剪模式更具侵略性,以便讓?xiě)?yīng)用程序變小。
由于使用未注釋反射的應(yīng)用程序的結(jié)果不可靠,修剪之前一直處于預(yù)覽狀態(tài)。有了修剪警告,體驗(yàn)現(xiàn)在應(yīng)該是可預(yù)測(cè)的。沒(méi)有修剪警告的應(yīng)用程序應(yīng)該正確修剪并且在運(yùn)行時(shí)觀察到行為沒(méi)有變化。目前,只有核心的 .NET 庫(kù)已經(jīng)完全注解了修剪,但我們希望看到生態(tài)系統(tǒng)注釋修剪并兼容修剪。
▌減小應(yīng)用程序大小
讓我們使用SDK 工具之一的crossgen來(lái)看看這個(gè)修剪改進(jìn)。它可以通過(guò)幾個(gè)修剪警告進(jìn)行修剪,crossgen 團(tuán)隊(duì)能夠解決。
首先,讓我們看一下將 crossgen 發(fā)布為一個(gè)獨(dú)立的應(yīng)用程序而無(wú)需修剪。它是 80 MB(包括 .NET 運(yùn)行時(shí)和所有庫(kù))。
然后我們可以嘗試(現(xiàn)在是舊版).NET 5 默認(rèn)修剪模式,copyused. 結(jié)果降至 55 MB。
新的 .NET 6 默認(rèn)修剪模式link將獨(dú)立文件大小進(jìn)一步降低到 36MB。
我們希望新的link修剪模式能更好地與修剪的期望保持一致:顯著節(jié)省和可預(yù)測(cè)的結(jié)果。
默認(rèn)啟用警告
修剪警告告訴您修剪可能會(huì)刪除運(yùn)行時(shí)使用的代碼的地方。這些警告以前默認(rèn)禁用,因?yàn)榫娣浅`须s,主要是由于 .NET 平臺(tái)沒(méi)有參與修剪作為第一類場(chǎng)景。
我們對(duì)大部分 .NET 庫(kù)進(jìn)行了注釋,以便它們產(chǎn)生準(zhǔn)確的修剪警告。因此,我們覺(jué)得是時(shí)候默認(rèn)啟用修剪警告了。ASP.NET Core 和 Windows 桌面運(yùn)行時(shí)庫(kù)尚未注釋。我們計(jì)劃接下來(lái)注釋 ASP.NET 服務(wù)組件(在 .NET 6 之后)。我們希望看到社區(qū)在 .NET 6 發(fā)布后對(duì) NuGet 庫(kù)進(jìn)行注釋。
您可以通過(guò)設(shè)置<SuppressTrimAnalysisWarnings>為true來(lái)禁用警告。
更多信息:
修剪警告
修剪介紹
準(zhǔn)備 .NET 庫(kù)以進(jìn)行修剪
與本機(jī) AOT 共享
我們也為Native AOT 實(shí)驗(yàn)實(shí)現(xiàn)了相同的修剪警告,這應(yīng)該會(huì)以幾乎相同的方式改善 Native AOT 編譯體驗(yàn)。
數(shù)學(xué)
我們顯著改進(jìn)了數(shù)學(xué) API。社區(qū)中的一些人已經(jīng)在享受這些改進(jìn)。
面向性能的 API
System.Math 中添加了面向性能的數(shù)學(xué) API。如果底層硬件支持,它們的實(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 變體返回 tuple。
性能改進(jìn):
ScaleB被移植到 C# 導(dǎo)致調(diào)用速度提高了 93%。感謝亞歷克斯·科文頓。
大整數(shù)性能
改進(jìn)了從十進(jìn)制和十六進(jìn)制字符串中解析 BigIntegers 。我們看到了高達(dá) 89% 的改進(jìn),如下圖所示(越低越好)。
感謝約瑟夫·達(dá)席爾瓦。
▌ComplexAPI 現(xiàn)在注釋為 readonly
現(xiàn)在對(duì)各種 API 進(jìn)行了注釋,System.Numerics.Complexreadonly 以確保不會(huì)對(duì)readonly值或傳遞的值進(jìn)行復(fù)制 in。
歸功于 hrrrrustic 。
▌BitConverter現(xiàn)在支持浮點(diǎn)到無(wú)符號(hào)整數(shù)位廣播
BitConverter 現(xiàn)在支持 DoubleToUInt64Bits, HalfToUInt16Bits, SingleToUInt32Bits, UInt16BitsToHalf, UInt32BitsToSingle, 和UInt64BitsToDouble. 這應(yīng)該使得在需要時(shí)更容易進(jìn)行浮點(diǎn)位操作。
歸功于 Michal Petryka 。
▌BitOperations支持附加功能
BitOperations現(xiàn)在支持IsPow2,RoundUpToPowerOf2和提供nint/nuint重載現(xiàn)有函數(shù)。
感謝約翰凱利、霍耀源和羅賓林德納。
▌Vector<T>, Vector2, Vector3, 和Vector4改進(jìn)
Vector<T> 現(xiàn)在支持C# 9 中添加的原始類型nint和nuint原始類型。例如,此更改應(yīng)該可以更簡(jiǎn)單地使用帶有指針或平臺(tái)相關(guān)長(zhǎng)度類型的 SIMD 指令。
Vector<T> 現(xiàn)在支持一種Sum方法來(lái)簡(jiǎn)化計(jì)算向量中所有元素的“水平和”的需要。歸功于伊萬(wàn)茲拉塔諾夫。
Vector<T> 現(xiàn)在支持一種通用方法As<TFrom, TTo>來(lái)簡(jiǎn)化在具體類型未知的通用上下文中處理向量。感謝霍耀源
重載支持Span<T>已添加到Vector2、Vector3和Vector4以改善需要加載或存儲(chǔ)矢量類型時(shí)的體驗(yàn)。
▌更好地解析標(biāo)準(zhǔn)數(shù)字格式
我們改進(jìn)了標(biāo)準(zhǔn)數(shù)字類型的解析器,特別是.ToString和.TryFormatParse。他們現(xiàn)在將理解對(duì)精度 > 99 位小數(shù)的要求,并將為那么多位數(shù)提供準(zhǔn)確的結(jié)果。此外,解析器現(xiàn)在更好地支持方法中的尾隨零。
以下示例演示了之前和之后的行為。
32.ToString("C100")->C132
.NET 6:$32.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
.NET 5:我們?cè)诟袷交a中人為限制只能處理 <= 99 的精度。對(duì)于精度 >= 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是無(wú)效的格式說(shuō)明符。所以,我們應(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。
System.Text.Json
System.Text.Json 提供多種高性能 API 用于處理 JSON 文檔。在過(guò)去的幾個(gè)版本中,我們添加了新功能,以進(jìn)一步提高 JSON 處理性能并減輕對(duì)希望從NewtonSoft.Json遷移的人的阻礙。此版本包括在該路徑上的繼續(xù),并且在性能方面向前邁出了一大步,特別是在序列化程序源生成器方面。
▌JsonSerializer 源生成
注意:使用 .NET 6 RC1 或更早版本的源代碼生成的應(yīng)用程序應(yīng)重新編譯。
幾乎所有 .NET 序列化程序的支柱都是反射。反射對(duì)于某些場(chǎng)景來(lái)說(shuō)是一種很好的能力,但不能作為高性能云原生應(yīng)用程序(通常(反)序列化和處理大量 JSON 文檔)的基礎(chǔ)。反射是啟動(dòng)、內(nèi)存使用和程序集修整的問(wèn)題。
運(yùn)行時(shí)反射的替代方法是編譯時(shí)源代碼生成。在 .NET 6 中,我們包含一個(gè)新的源代碼生成器作為System.Text.Json. JSON 源代碼生成器可以與多種方式結(jié)合使用JsonSerializer并且可以通過(guò)多種方式進(jìn)行配置。
它可以提供以下好處:
? 減少啟動(dòng)時(shí)間
? 提高序列化吞吐量
? 減少私有內(nèi)存使用
? 刪除運(yùn)行時(shí)使用System.Reflection 和System.Reflection.Emit
? IL 修整兼容性
默認(rèn)情況下,JSON 源生成器為給定的可序列化類型發(fā)出序列化邏輯。JsonSerializer通過(guò)生成直接使用的源代碼,這提供了比使用現(xiàn)有方法更高的性能Utf8JsonWriter。簡(jiǎn)而言之,源代碼生成器提供了一種在編譯時(shí)為您提供不同實(shí)現(xiàn)的方法,以使運(yùn)行時(shí)體驗(yàn)更好。
給定一個(gè)簡(jiǎn)單的類型:
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)用可能類似于以下示例。此示例提供了可能的最佳性能。
最快和最優(yōu)化的源代碼生成模式——基于Utf8JsonWriter——目前僅可用于序列化。Utf8JsonReader根據(jù)您的反饋,將來(lái)可能會(huì)提供對(duì)反序列化的類似支持。
源生成器還發(fā)出類型元數(shù)據(jù)初始化邏輯,這也有利于反序列化。JsonMessage要反序列化使用預(yù)生成類型元數(shù)據(jù)的實(shí)例,您可以執(zhí)行以下操作:
JsonSerializer.Deserialize(json, JsonContext.Default.JsonMessage);▌JsonSerializer 支持 IAsyncEnumerable
您現(xiàn)在可以使用System.Text.Json (反)序列化IAsyncEnumerable<T>JSON數(shù)組。以下示例使用流作為任何異步數(shù)據(jù)源的表示。源可以是本地計(jì)算機(jī)上的文件,也可以是數(shù)據(jù)庫(kù)查詢或 Web 服務(wù) API 調(diào)用的結(jié)果。
JsonSerializer.SerializeAsync已更新以識(shí)別并為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 僅使用異步序列化方法支持值。嘗試使用同步方法進(jìn)行序列化將導(dǎo)致NotSupportedException被拋出。
流式反序列化需要一個(gè)新的 API 來(lái)返回 I AsyncEnumerable<T>. 我們?yōu)榇颂砑恿薐sonSerializer.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); }此示例將按需反序列化元素,并且在使用特別大的數(shù)據(jù)流時(shí)非常有用。它僅支持從根級(jí) JSON 數(shù)組讀取,盡管將來(lái)可能會(huì)根據(jù)反饋放寬。
現(xiàn)有 DeserializeAsync 方法名義上支持 I AsyncEnumerable<T>,但在其非流方法簽名的范圍內(nèi)。它必須將最終結(jié)果作為單個(gè)值返回,如以下示例所示。
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 在返回反序列化對(duì)象之前緩沖內(nèi)存中的所有內(nèi)容。這是因?yàn)榉葱蛄谢餍枰诜祷亟Y(jié)果之前消耗整個(gè) JSON 值。
▌System.Text.Json:可寫(xiě) DOM 功能
可寫(xiě) JSON DOM特性為 System.Text.Json添加了一個(gè)新的簡(jiǎn)單且高性能的編程模型。這個(gè)新的 API 很有吸引力,因?yàn)樗苊饬诵枰獜?qiáng)類型的序列化合約,并且與現(xiàn)有的 JsonDocument類型相比,DOM 是可變的。
這個(gè)新的 API 有以下好處:
在使用POCO類型是不可能或不希望的情況下,或者當(dāng) JSON 模式不固定且必須檢查的情況下,序列化的輕量級(jí)替代方案。
啟用對(duì)大樹(shù)子集的有效修改。例如,可以有效地導(dǎo)航到大型 JSON 樹(shù)的子部分并從該子部分讀取數(shù)組或反序列化 POCO。LINQ 也可以與它一起使用。
以下示例演示了新的編程模型。
▌ReferenceHandler.IgnoreCycles
JsonSerializer(System.Text.Json)現(xiàn)在支持在序列化對(duì)象圖時(shí)忽略循環(huán)的能力。該ReferenceHandler.IgnoreCycles選項(xiàng)具有與 Newtonsoft.Json ReferenceLoopHandling.Ignore類似的行為。一個(gè)關(guān)鍵區(qū)別是 System.Text.Json 實(shí)現(xiàn)用null JSON 標(biāo)記替換引用循環(huán),而不是忽略對(duì)象引用。
您可以在以下示例中看到ReferenceHandler.IgnoreCycles 的行為。在這種情況下,該Next屬性被序列化為null,因?yàn)榉駝t它會(huì)創(chuàng)建一個(gè)循環(huán)。
class Node {public string Description { get; set; }public object Next { get; set; } }void Test() {var node = new Node { Description = "Node 1" };node.Next = node;var opts = new JsonSerializerOptions { ReferenceHandler = ReferenceHandler.IgnoreCycles };string json = JsonSerializer.Serialize(node, opts);Console.WriteLine(json); // Prints {"Description":"Node 1","Next":null} }源代碼構(gòu)建
通過(guò)源代碼構(gòu)建,您只需幾個(gè)命令即可在您自己的計(jì)算機(jī)上從源代碼構(gòu)建 .NET SDK 。讓我解釋一下為什么這個(gè)項(xiàng)目很重要。
源代碼構(gòu)建是一個(gè)場(chǎng)景,也是我們?cè)诎l(fā)布 .NET Core 1.0 之前一直與 Red Hat 合作開(kāi)發(fā)的基礎(chǔ)架構(gòu)。幾年后,我們非常接近于交付它的全自動(dòng)版本。對(duì)于 Red Hat Enterprise Linux (RHEL) .NET 用戶來(lái)說(shuō),這個(gè)功能很重要。Red Hat 告訴我們,.NET 已經(jīng)發(fā)展成為其生態(tài)系統(tǒng)的重要開(kāi)發(fā)者平臺(tái)。好的!
Linux 發(fā)行版的黃金標(biāo)準(zhǔn)是使用作為發(fā)行版存檔一部分的編譯器和工具鏈構(gòu)建開(kāi)源代碼。這適用于 .NET 運(yùn)行時(shí)(用 C++ 編寫(xiě)),但不適用于任何用 C# 編寫(xiě)的代碼。對(duì)于 C# 代碼,我們使用兩遍構(gòu)建機(jī)制來(lái)滿足發(fā)行版要求。這有點(diǎn)復(fù)雜,但了解流程很重要。
Red Hat 使用 .NET SDK (#1) 的 Microsoft 二進(jìn)制構(gòu)建來(lái)構(gòu)建 .NET SDK 源代碼,以生成 SDK (#2) 的純開(kāi)源二進(jìn)制構(gòu)建。之后,使用這個(gè)新版本的 SDK (#2) 再次構(gòu)建相同的 SDK 源代碼,以生成可證明的開(kāi)源 SDK (#3)。.NET SDK (#3) 的最終二進(jìn)制版本隨后可供 RHEL 用戶使用。之后,Red Hat 可以使用相同的 SDK (#3) 來(lái)構(gòu)建新的 .NET 版本,而不再需要使用 Microsoft SDK 來(lái)構(gòu)建每月更新。
這個(gè)過(guò)程可能令人驚訝和困惑。開(kāi)源發(fā)行版需要通過(guò)開(kāi)源工具構(gòu)建。此模式確保不需要 Microsoft 構(gòu)建的 SDK,無(wú)論是有意還是無(wú)意。作為開(kāi)發(fā)者平臺(tái),包含在發(fā)行版中的門(mén)檻比僅使用兼容許可證的門(mén)檻更高。源代碼構(gòu)建項(xiàng)目使 .NET 能夠滿足該標(biāo)準(zhǔn)。
源代碼構(gòu)建的可交付成果是源代碼壓縮包。源 tarball 包含 SDK 的所有源(對(duì)于給定版本)。從那里,紅帽(或其他組織)可以構(gòu)建自己的 SDK 版本。Red Hat 政策要求使用內(nèi)置源工具鏈來(lái)生成二進(jìn)制 tar 球,這就是他們使用兩遍方法的原因。但是源代碼構(gòu)建本身不需要這種兩遍方法。
在 Linux 生態(tài)系統(tǒng)中,給定組件同時(shí)擁有源和二進(jìn)制包或 tarball 是很常見(jiàn)的。我們已經(jīng)有了可用的二進(jìn)制 tarball,現(xiàn)在也有了源 tarball。這使得 .NET 與標(biāo)準(zhǔn)組件模式相匹配。
.NET 6 的重大改進(jìn)是源 tarball 現(xiàn)在是我們構(gòu)建的產(chǎn)品。它過(guò)去需要大量的人工來(lái)制作,這也導(dǎo)致將源 tarball 交付給 Red Hat 的延遲很長(zhǎng)。雙方都對(duì)此不滿意。
在這個(gè)項(xiàng)目上,我們與紅帽密切合作五年多。它的成功在很大程度上要?dú)w功于我們有幸與之共事的優(yōu)秀紅帽工程師的努力。其他發(fā)行版和組織已經(jīng)并將從他們的努力中受益。
附帶說(shuō)明一下,源代碼構(gòu)建是朝著可重現(xiàn)構(gòu)建邁出的一大步,我們也堅(jiān)信這一點(diǎn)。.NET SDK 和 C# 編譯器具有重要的可重現(xiàn)構(gòu)建功能。
庫(kù) API
除了已經(jīng)涵蓋的 API 之外,還添加了以下 API。
▌WebSocket 壓縮
壓縮對(duì)于通過(guò)網(wǎng)絡(luò)傳輸?shù)娜魏螖?shù)據(jù)都很重要。WebSockets 現(xiàn)在啟用壓縮。我們使用了WebSockets 的擴(kuò)展permessage-deflate實(shí)現(xiàn),RFC 7692。它允許使用該DEFLATE算法壓縮 WebSockets 消息負(fù)載。此功能是 GitHub 上 Networking 的主要用戶請(qǐng)求之一。
與加密一起使用的壓縮可能會(huì)導(dǎo)致攻擊,例如CRIME和BREACH。這意味著不能在單個(gè)壓縮上下文中將秘密與用戶生成的數(shù)據(jù)一起發(fā)送,否則可以提取該秘密。為了讓用戶注意到這些影響并幫助他們權(quán)衡風(fēng)險(xiǎn),我們將其中一個(gè)關(guān)鍵 API 命名為DangerousDeflateOptions。我們還添加了關(guān)閉特定消息壓縮的功能,因此如果用戶想要發(fā)送秘密,他們可以在不壓縮的情況下安全地執(zhí)行此操作。
禁用壓縮時(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 壓縮支持。
歸功于伊萬(wàn)茲拉塔諾夫。
▌Socks 代理支持
SOCKS是一種代理服務(wù)器實(shí)現(xiàn),可以處理任何 TCP 或 UDP 流量,使其成為一個(gè)非常通用的系統(tǒng)。這是一個(gè)長(zhǎng)期存在的社區(qū)請(qǐng)求,已添加到 .NET 6中。
此更改增加了對(duì) Socks4、Socks4a 和 Socks5 的支持。例如,它可以通過(guò) SSH 測(cè)試外部連接或連接到 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);歸功于 Huo yaoyuan。
▌Microsoft.Extensions.Hosting — 配置主機(jī)選項(xiàng) API
我們?cè)?IHostBuilder 上添加了一個(gè)新的 ConfigureHostOptions API,以簡(jiǎn)化應(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<HostOptions>(o =>{o.ShutdownTimeout = TimeSpan.FromMinutes(10);});}).Build();host.Run();▌Microsoft.Extensions.DependencyInjection — CreateAsyncScope API
CreateAsyncScope 創(chuàng)建API是為了處理服務(wù)的處置 IAsyncDisposable。以前,您可能已經(jīng)注意到處置 IAsyncDisposable 服務(wù)提供者可能會(huì)引發(fā) InvalidOperationException 異常。
以下示例演示了新模式,CreateAsyncScope 用于啟用 using 語(yǔ)句的安全使用。
await using (var scope = provider.CreateAsyncScope()) {var foo = scope.ServiceProvider.GetRequiredService<Foo>(); }以下示例演示了現(xiàn)有的問(wèn)題案例:
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 — 編譯時(shí)源生成器
.NET 6引入了LoggerMessageAttribute類型. 此屬性是Microsoft.Extensions.Logging 命名空間的一部分,使用時(shí),它會(huì)源生成高性能日志記錄 API。源生成日志支持旨在為現(xiàn)代 .NET 應(yīng)用程序提供高度可用和高性能的日志解決方案。自動(dòng)生成的源代碼依賴于 ILogger 接口和 LoggerMessage.Define功能。
LoggerMessageAttribute 源生成器在用于 partial 日志記錄方法時(shí)觸發(fā)。當(dāng)被觸發(fā)時(shí),它要么能夠自動(dòng)生成 partial 它正在裝飾的方法的實(shí)現(xiàn),要么生成編譯時(shí)診斷,并提供有關(guān)正確使用的提示。編譯時(shí)日志記錄解決方案在運(yùn)行時(shí)通常比現(xiàn)有的日志記錄方法快得多。它通過(guò)最大限度地消除裝箱、臨時(shí)分配和副本來(lái)實(shí)現(xiàn)這一點(diǎn)。
與直接手動(dòng)使用 LoggerMessage.Define API 相比,有以下好處:
更短更簡(jiǎn)單的語(yǔ)法:聲明性屬性使用而不是編碼樣板。
引導(dǎo)式開(kāi)發(fā)人員體驗(yàn):生成器發(fā)出警告以幫助開(kāi)發(fā)人員做正確的事情。
支持任意數(shù)量的日志記錄參數(shù)。LoggerMessage.Define 最多支持六個(gè)。
支持動(dòng)態(tài)日志級(jí)別。這是 LoggerMessage.Define 單獨(dú)不可能的。
要使用 LoggerMessageAttribute,消費(fèi)類和方法需要是 partial。代碼生成器在編譯時(shí)觸發(fā)并生成 partial 方法的實(shí)現(xiàn)。
在前面的示例中,日志記錄方法是 static,并且在屬性定義中指定了日志級(jí)別。在靜態(tài)上下文中使用屬性時(shí),ILogger 需要實(shí)例作為參數(shù)。您也可以選擇在非靜態(tài)上下文中使用該屬性。有關(guān)更多示例和使用場(chǎng)景,請(qǐng)?jiān)L問(wèn)編譯時(shí)日志記錄源生成器文檔。
▌System.Linq — 可枚舉的支持 Index 和 Range 參數(shù)
該 Enumerable.ElementAt 方法現(xiàn)在接受來(lái)自可枚舉末尾的索引,如以下示例所示。
Enumerable.Range(1, 10).ElementAt(^2); // returns 9
添加了一個(gè) Enumerable.Take 接受 Range 參數(shù)的重載。它簡(jiǎn)化了對(duì)可枚舉序列的切片:
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ū)很有用的場(chǎng)景中很有用,如下面的示例所示。
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 檢查實(shí)現(xiàn) ICollection/ ICollection<T>或利用Linq 采用的一些內(nèi)部?jī)?yōu)化的源。
▌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<int[]> chunks = Enumerable.Range(0, 10).Chunk(size: 3); // { {0,1,2}, {3,4,5}, {6,7,8}, {9} }歸功于羅伯特安德森。
▌System.Linq—— //FirstOrDefault 采用默認(rèn)參數(shù)的重載LastOrDefaultSingleOrDefault
如果源可枚舉為空,則現(xiàn)有的 FirstOrDefault /LastOrDefault /SingleOrDefault 方法返回 default(T)。添加了新的重載,它們接受在這種情況下返回的默認(rèn)參數(shù),如以下示例所示。?
感謝@ Foxtrek64 。
▌System.Linq —Zip 接受三個(gè)可枚舉的重載
Zip方法現(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)){}歸功于 Huo yaoyuan 。
▌優(yōu)先隊(duì)列
PriorityQueue<TElement, TPriority>(System.Collections.Generic) 是一個(gè)新集合,可以添加具有值和優(yōu)先級(jí)的新項(xiàng)目。在出隊(duì)時(shí),PriorityQueue 返回具有最低優(yōu)先級(jí)值的元素。您可以認(rèn)為這個(gè)新集合類似于Queue<T>但每個(gè)入隊(duì)元素都有一個(gè)影響出隊(duì)行為的優(yōu)先級(jí)值。
以下示例演示了.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。
精彩攻略歡迎繼續(xù)轉(zhuǎn)到下篇文章,繼續(xù)閱讀哦!
往期精彩回顧
▎.NET 6 攻略大全(一)
▎.NET 6 攻略大全(二)
?了解更多.NET?
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的.NET 6 攻略大全(三)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: .NET 6 攻略大全(二)
- 下一篇: 揭秘.NET Core剪裁器背后的技术