日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

.NET 5.0 RC1 发布,离正式版发布仅剩两个版本,与 netty 相比更具竞争力

發(fā)布時間:2023/12/4 asp.net 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 .NET 5.0 RC1 发布,离正式版发布仅剩两个版本,与 netty 相比更具竞争力 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

原文:http://dwz.win/Qf8
作者:Richard
翻譯:精致碼農(nóng)-王亮
說明:
1. 本譯文并不是完全逐句翻譯的,存在部分語句我實在不知道如何翻譯或組織就根據(jù)個人理解用自己的話表述了。
2. 本文有不少超鏈接,由于微信公眾號和頭條平臺外鏈會被剔除 URL 地址,所以原來本是超鏈接的內(nèi)容會顯示為純文本,如果你需要這些信息你可以移步到我的知乎博客園閱讀(搜索精致碼農(nóng)可找到我)。

今天我們發(fā)布了 .NET 5.0 Release Candidate 1 (RC1)。它是目前最接近 .NET 5.0 的一個版本,也是在 11 月正式發(fā)布之前的兩個 RC 版本中的第一個 RC 版本。RC1 是一個“上線”版本,表示你可以在生產(chǎn)環(huán)境中使用它了。

與此同時,我們一直在尋找最終正式版發(fā)布之前應(yīng)該被修復(fù)的任何關(guān)鍵錯誤報告。我們需要你的反饋來幫助我們一起跨越 .NET 5.0 正式發(fā)布這道勝利的終點線。

我們今天也發(fā)布了 ASP.NET Core 和 EF Core 的 RC1 版本。

你可以下載適用于 Windows、macOS 和 Linux 的 .NET 5.0 版本:

  • 安裝程序和二進(jìn)制包

  • 容器鏡像

  • 快速安裝程序

  • 發(fā)布說明

  • 已知問題

  • GitHub Issue 跟蹤

你需要最新的預(yù)覽版 Visual Studio (包括 Visual Studio for Mac) 才能使用 .NET 5.0。

.NET 5.0 有很多改進(jìn),特別是單個文件應(yīng)用程序、更小的容器映像、更強(qiáng)大的 JsonSerializer API、完整的可空引用類型標(biāo)注、新的目標(biāo) Framework 名稱,以及對 Windows ARM64 的支持。在網(wǎng)絡(luò)庫、GC 和 JIT 中性能得到了極大的提高。我們花了很大的工作在 ARM64 的性能上,它有了更好的吞吐量和更小的二進(jìn)制文件。.NET 5.0 包含了新的語言版本:C# 9.0 和 F# 5.0。

我們最近發(fā)布了一些關(guān)于 5.0 新功能深入介紹的文章,你可能想看一看這些文章:

  • F# 5 update for August

  • ARM64 Performance in .NET 5

  • Improvements in native code interop in .NET 5.0

  • Introducing the Half type!

  • App Trimming in .NET 5

  • Customizing Trimming in .NET 5

  • Automatically find latent bugs in your code with .NET 5

就像我在 .NET 5.0 預(yù)覽 8 文中所做的一樣,我選擇了一些特性來進(jìn)行更深入的介紹,并讓你了解如何在實際使用中使用它們。這篇文章專門討論 C# 9 中的 System.Text.Json.JsonSerializer 和 records(記錄)。它們是獨(dú)立的特性,但也是很好的組合,特別是如果你花費(fèi)大量時間為反序列化的 JSON 對象創(chuàng)建 POCO 類型。

C# 9 — 記錄

記錄(原文 Record)可能是 C# 9 中最重要的新特性。它們提供了廣泛的特性集(對一種語言類型來說),其中一些需要 RC1 或更高版本(如 record.ToString())。

譯注:為了閱讀更通順,對 Record 的翻譯,本譯文根據(jù)語境的情況,有的地方用的是“Record”,有的地方用的是“記錄”。因為在一些語境下把“Record”翻譯成“記錄”容易產(chǎn)生數(shù)據(jù)記錄的錯誤聯(lián)想。

最簡單的理解,記錄是不可變類型。在特性方面,它們最接近元組(Tuple),可以將它們視為具有屬性且不可變的自定義元組。在如今使用元組的多數(shù)情況下,記錄可以比元組提供更好更多的功能和使用場景。

在使用 C# 時,如果你使用命名類型會使你得到最好的體驗(相對于像元組這樣的特性)。靜態(tài)類型(static typing)是該語言的設(shè)計要點,記錄使小型類型更容易使用,并在整個應(yīng)用程序中可以保證類型安全。

記錄是不可變數(shù)據(jù)類型

記錄使你能夠創(chuàng)建不可變的數(shù)據(jù)類型,這對于定義存儲少量數(shù)據(jù)的類型非常有用。

下面是一個記錄的例子,它存儲登錄用戶信息。

public?record?LoginResource(string?Username,?string?Password,?bool?RememberMe);

它在語義上與下面的類相似(幾乎完全相同),我即將介紹這些差異。

public?class?LoginResource {public?LoginResource(string?username,?string?password,?bool?rememberMe){Username?=?username;Password?=?password;RememberMe?=?rememberMe;}public?string?Username?{?get;?init;?}public?string?Password?{?get;?init;?}public?bool?RememberMe?{?get;?init;?} }

init 是一個新的關(guān)鍵字,它是 set 的替代。set 允許你在任何時候給屬性分配值,init 只允許在對象構(gòu)造期間給屬性賦值,它是記錄不變性所依賴的基石。任何類型都可以使用 init,正如你在前面的類定義中看到的那樣,它并不局限于在記錄中使用。

private set 看起來類似于 init,private set 防止其他代碼(類型以外的代碼)改變數(shù)據(jù)。當(dāng)類型(在構(gòu)造之后)意外地改變屬性時,init 將產(chǎn)生編譯錯誤。private set 不能使數(shù)據(jù)不可變,因此當(dāng)類型在構(gòu)造后改變屬性值時,不會生成任何編譯錯誤或警告。

記錄是特殊的類

正如我剛才提到的,LoginResource 的記錄變體和類變體幾乎是相同的。類定義是記錄的一個語義相同的子集,記錄提供了更多特殊的行為。

為了讓我們的想法達(dá)成一致,如前所述,下面的比較是一個記錄和一個使用 init 代替 set 修飾屬性的類之間的區(qū)別。

有哪些共同點:

  • 構(gòu)造函數(shù)

  • 不變性

  • 復(fù)制語義(記錄本質(zhì)是類)

有哪些不同點:

  • 記錄相等是基于內(nèi)容的,類相等是基于對象標(biāo)識;

  • 記錄提供了一個基于內(nèi)容 GetHashCode() 實現(xiàn);

  • 記錄提供了一個IEquatable<T>的實現(xiàn),它使用 GetHashCode() 唯一性作為行為機(jī)制,為記錄提供基于內(nèi)容的相等語義;

  • 記錄重寫(override)了 ToString(),打印的是記錄的內(nèi)容。

記錄和(使用 init 的)類之間的差異可以在 LoginResource 作為記錄和 LoginResource 作為類的反編譯代碼中可以看到。

我將向你展示一些有差異的代碼:

using?System; using?System.Linq; using?static?System.Console;var?user?=?"Lion-O"; var?password?=?"jaga"; var?rememberMe?=?true; LoginResourceRecord?lrr1?=?new(user,?password,?rememberMe); var?lrr2?=?new?LoginResourceRecord(user,?password,?rememberMe); var?lrc1?=?new?LoginResourceClass(user,?password,?rememberMe); var?lrc2?=?new?LoginResourceClass(user,?password,?rememberMe);WriteLine($"Test?record?equality?--?lrr1?==?lrr2?:?{lrr1?==?lrr2}"); WriteLine($"Test?class?equality??--?lrc1?==?lrc2?:?{lrc1?==?lrc2}"); WriteLine($"Print?lrr1?hash?code?--?lrr1.GetHashCode():?{lrr1.GetHashCode()}"); WriteLine($"Print?lrr2?hash?code?--?lrr2.GetHashCode():?{lrr2.GetHashCode()}"); WriteLine($"Print?lrc1?hash?code?--?lrc1.GetHashCode():?{lrc1.GetHashCode()}"); WriteLine($"Print?lrc2?hash?code?--?lrc2.GetHashCode():?{lrc2.GetHashCode()}"); WriteLine($"{nameof(LoginResourceRecord)}?implements?IEquatable<T>:?{lrr1?is?IEquatable<LoginResourceRecord>}?"); WriteLine($"{nameof(LoginResourceClass)}??implements?IEquatable<T>:?{lrr1?is?IEquatable<LoginResourceClass>}"); WriteLine($"Print?{nameof(LoginResourceRecord)}.ToString?--?lrr1.ToString():?{lrr1.ToString()}"); WriteLine($"Print?{nameof(LoginResourceClass)}.ToString??--?lrc1.ToString():?{lrc1.ToString()}");public?record?LoginResourceRecord(string?Username,?string?Password,?bool?RememberMe);public?class?LoginResourceClass {public?LoginResourceClass(string?username,?string?password,?bool?rememberMe){Username?=?username;Password?=?password;RememberMe?=?rememberMe;}public?string?Username?{?get;?init;?}public?string?Password?{?get;?init;?}public?bool?RememberMe?{?get;?init;?} }

注意:你將注意到 LoginResource 類型以 Record 和 Class 結(jié)束,該模式并不是新的命名約定,這樣命名只是為了在樣本中有相同類型的記錄和類變體,請不要這樣命名你的類。

此代碼的輸出如下:

rich@thundera?records?%?dotnet?run Test?record?equality?--?lrr1?==?lrr2?:?True Test?class?equality??--?lrc1?==?lrc2?:?False Print?lrr1?hash?code?--?lrr1.GetHashCode():?-542976961 Print?lrr2?hash?code?--?lrr2.GetHashCode():?-542976961 Print?lrc1?hash?code?--?lrc1.GetHashCode():?54267293 Print?lrc2?hash?code?--?lrc2.GetHashCode():?18643596 LoginResourceRecord?implements?IEquatable<T>:?True LoginResourceClass??implements?IEquatable<T>:?False Print?LoginResourceRecord.ToString?--?lrr1.ToString():?LoginResourceRecord?{?Username?=?Lion-O,?Password?=?jaga,?RememberMe?=?True?} Print?LoginResourceClass.ToString?--?lrc1.ToString():?LoginResourceClass

記錄的語法

有多種用于聲明記錄的模式,用于滿足不同場景的使用。在玩過每個模式之后,你開始會對每種模式的好處有一個感性的認(rèn)識。你還將看到,它們不是不同的語法,而是選項的連續(xù)體(continuum of options)。

第一個模式是最簡單的 —— 一行代碼 —— 但是提供的靈活性最小,它適用于具有少量必需屬性(必需屬性,即初始化時必需給作為參數(shù)的屬性傳值)的記錄。

以下用前面展示的 LoginResource 記錄作為此模式的一個示例。就這么簡單,一行代碼就是整個定義:

public?record?LoginResource(string?Username,?string?Password,?bool?RememberMe);

構(gòu)造遵循帶參數(shù)的構(gòu)造函數(shù)的要求(包括允許使用可選參數(shù)):

var?login?=?new?LoginResource("Lion-O",?"jaga",?true);

如果你喜歡,也可以用 target typing:

LoginResource?login?=?new("Lion-O",?"jaga",?true);

下面這個語法使所有屬性都是可選的,為記錄提供了一個隱式無參數(shù)構(gòu)造函數(shù)。

public?record?LoginResource {public?string?Username?{get;?init;}public?string?Password?{get;?init;}public?bool?RememberMe?{get;?init;} }

使用對象初始化構(gòu)造,可以像下面這樣:

LoginResource?login?=?new() {Username?=?"Lion-O",TemperatureC?=?"jaga" };

如果你想讓這兩個屬性成為必需的,而另一個屬性是可選的,這最后一個模式如下所示:

public?record?LoginResource(string?Username,?string?Password) {public?bool?RememberMe?{get;?init;} }

可以像下面這樣不指定 RememberMe 構(gòu)造:

LoginResource?login?=?new("Lion-O",?"jaga");

也可以指定 RememberMe 構(gòu)造:

LoginResource?login?=?new("Lion-O",?"jaga") {RememberMe?=?true };

不要認(rèn)為記錄只用于不可變數(shù)據(jù)。你可以置入公開可變屬性,如下面的示例所示,該示例報告了關(guān)于電池的信息。Model 和 TotalCapacityAmpHours 屬性是不可變的,而 RemainingCapacityPercentange 是可變的。

using?System;Battery?battery?=?new?Battery("CR2032",?0.235) {RemainingCapacityPercentage?=?100 };Console.WriteLine?(battery);for?(int?i?=?battery.RemainingCapacityPercentage;?i?>=?0;?i--) {battery.RemainingCapacityPercentage?=?i; }Console.WriteLine?(battery);public?record?Battery(string?Model,?double?TotalCapacityAmpHours) {public?int?RemainingCapacityPercentage?{get;set;} }

它輸出如下結(jié)果:

rich@thundera?recordmutable?%?dotnet?run Battery?{?Model?=?CR2032,?TotalCapacityAmpHours?=?0.235,?RemainingCapacityPercentage?=?100?} Battery?{?Model?=?CR2032,?TotalCapacityAmpHours?=?0.235,?RemainingCapacityPercentage?=?0?}

無損式記錄修改

不變性提供了顯著的好處,但是您很快就會發(fā)現(xiàn)需要對記錄進(jìn)行改變的情況。你怎么能在不放棄不變性的前提下做到這一點呢?with 表達(dá)式滿足了這一需求。它支持根據(jù)相同類型的現(xiàn)有記錄創(chuàng)建新記錄。你可以指定你想要的不同的新值,并且從現(xiàn)有記錄復(fù)制所有其他屬性。

讓我們把用戶名轉(zhuǎn)換成小寫,這是用戶名在我們假定的一個用戶數(shù)據(jù)庫中的存儲方式。但是,為了進(jìn)行診斷,需要使用原始用戶名大小寫。假設(shè)以前面示例中的代碼為例,它可能像下面這樣:

LoginResource?login?=?new("Lion-O",?"jaga",?true); LoginResource?loginLowercased?=?lrr1?with?{Username?=?login.Username.ToLowerInvariant()};

login 記錄沒有被更改,事實上這也是不允許的。轉(zhuǎn)換只影響了 loginLowercased,除了將小寫轉(zhuǎn)換為 loginLowercased 之外,其它與 login 是相同的。

我們可以使用內(nèi)置的 ToString() 檢查 width 是否完成了預(yù)期的工作:

Console.WriteLine(login); Console.WriteLine(loginLowercased);

此代碼輸出如下結(jié)果:

LoginResource?{?Username?=?Lion-O,?Password?=?jaga,?RememberMe?=?True?} LoginResource?{?Username?=?lion-o,?Password?=?jaga,?RememberMe?=?True?}

我們可以進(jìn)一步了解 with 是如何工作的,它將所有值從一條記錄復(fù)制到另一條記錄。這不是一個記錄依賴于另一個記錄的模型。事實上,with 操作完成后,兩個記錄之間就沒有關(guān)系了,只在對記錄的構(gòu)建時有意義。這意味著對于引用類型,副本只是引用的副本;對于值類型,是復(fù)制值。

你可以在下面的代碼中看到這種語義:

Console.WriteLine($"Record?equality:?{login?==?loginLowercased}"); Console.WriteLine($"Property?equality:?Username?==?{login.Username?==?loginLowercased.Username};?Password?==?{login.Password?==?loginLowercased.Password};?RememberMe?==?{login.RememberMe?==?loginLowercased.RememberMe}");

它輸出如下結(jié)果:

Record?equality:?False Property?equality:?Username?==?False;?Password?==?True;?RememberMe?==?True

記錄的實例

對記錄進(jìn)行擴(kuò)展是很容易的。讓我們假設(shè)一個新的 LastLoggedIn 屬性,它可以直接添加到 LoginResource。那是個好的設(shè)想,記錄不像傳統(tǒng)的接口那樣脆弱,除非你想讓該新屬性在創(chuàng)建時作為構(gòu)造函數(shù)所必需的參數(shù)。

在這個案例中,現(xiàn)在我想使 LastLoggedIn 是必需的。想象一下,代碼庫非常大,把這個修改反應(yīng)到所有創(chuàng)建 LoginResource 的地方工作量是巨大的。相反,我們將用這個新屬性創(chuàng)建一個擴(kuò)展 LoginResource 的新 Record。現(xiàn)有代碼將在 LoginResource 方面工作,新代碼將在新 Record 上工作,然后可以假設(shè) LastLoggedIn 屬性已經(jīng)賦值。根據(jù)常規(guī)繼承規(guī)則,接受 LoginResource 的代碼將同樣輕松地接受新的 Record。

這個新 Record 可以基于前面演示的任何 LoginResource 變體,它將基于以下內(nèi)容:

public?record?LoginResource(string?Username,?string?Password) {public?bool?RememberMe?{get;?init;} }

新的 Record 將是如下這樣的:

public?record?LoginWithUserDataResource(string?Username,?string?Password,?DateTime?LastLoggedIn)?:?LoginResource(Username,?Password) {public?int?DiscountTier?{get;?init};public?bool?FreeShipping?{get;?init}; }

我將 LastLoggedIn 設(shè)置為一個必需的屬性,并利用這個機(jī)會添加了附加的且可選的屬性,這些屬性可能設(shè)置也可能沒有設(shè)置值。通過擴(kuò)展 LoginResource 記錄,還定義了可選的 RememberMe 屬性。

記錄的構(gòu)造輔助

其中一個不是很直觀的模式是建模輔助(modeling helpers),你希望使用它作為記錄構(gòu)造的一部分(譯注:用來輔助創(chuàng)建記錄實例)。讓我們來換個體重測量的示例。體重的測量用的是一個聯(lián)網(wǎng)的秤,重量以公斤為單位,但是在某些情況下,體重需要以磅作為單位顯示。

可以使用以下記錄聲明:

public?record?WeightMeasurement(DateTime?Date,?int?Kilograms) {public?int?Pounds?{get;?init;}public?static?int?GetPounds(int?kilograms)?=>?kilograms?*?2.20462262; }

對應(yīng)的構(gòu)造是這樣的:

var?weight?=?200; WeightMeasurement?measurement?=?new(DateTime.Now,?weight) {Pounds?=?WeightMeasurement.GetPounds(weight) };

在本例中,需要說明的是 weight 是本地變量,不可能在對象初始化器中訪問 Kilograms 屬性。也有必要將 GetPounds 定義為靜態(tài)方法,因為不可能在對象初始化器中調(diào)用實例(它還未構(gòu)造完成)方法。

記錄和可空性

語法上,記錄是具有可空性(Nullability)的對嗎?既然記錄是不可變的,那 null 從何而來呢?如果初始值就是 null,那就一直是 null,這樣的數(shù)據(jù)有什么意義呢?

讓我們來看一個沒有使用可空性的程序:

using?System; using?System.Collections.Generic;Author?author?=?new(null,?null);Console.WriteLine(author.Name.ToString());public?record?Author(string?Name,?List<Book>?Books) {public?string?Website?{get;?init;}public?string?Genre?{get;?init;}public?List<Author>?RelatedAuthors?{get;?init;} }public?record?Book(string?name,?int?Published,?Author?author);

這個程序編譯時將拋出一個 NullReference 異常,因為 author.Name 是 null(譯者疑問:真的是編譯時報錯而不是運(yùn)行時報錯嗎?期待大家親測)。

為了更進(jìn)一步說明這一點,下面的代碼無法編譯通過,因為 author.Name 初始值為 null,然后是不能更改的,因為屬性是不可變的。

Author?author?=?new(null,?null); author.Name?=?"Colin?Meloy";

我要更新我的 project 文件,以啟用可空性。

<Project?Sdk="Microsoft.NET.Sdk"><PropertyGroup><OutputType>Exe</OutputType><TargetFramework>net5.0</TargetFramework><LangVersion>preview</LangVersion><Nullable>enable</Nullable></PropertyGroup></Project>

我現(xiàn)在看到如下的一堆警告:

/Users/rich/recordsnullability/Program.cs(8,21):?warning?CS8618:?Non-nullable?property?'Website'?\n must?contain?a?non-null?value?when?exiting?constructor.?Consider?declaring?the?property?as?\n nullable.?[/Users/rich/recordsnullability/recordsnullability.csproj]

我用我用可空修飾符更新了 Author 記錄,這些可空修飾符描述了我打算如何使用該記錄。

public?record?Author(string?Name,?List<Book>?Books) {public?string??Website?{get;?init;}public?string??Genre?{get;?init;}public?List<Author>??RelatedAuthors?{get;?init;} }

我仍然得到了關(guān)于 null 的警告,之前看到的 Author 的 null 構(gòu)造。

/Users/rich/recordsnullability/Program.cs(5,21):?warning?CS8625:?Cannot?convert?null?literal?\n to?non-nullable?reference?type.?[/Users/rich/recordsnullability/recordsnullability.csproj]

這很好,因為這是我想防止的情況。現(xiàn)在,我將向你展示這個程序的一個更新版本,它很好地利用了可空性的好處。

using?System; using?System.Collections.Generic; using?System.Diagnostics.CodeAnalysis;Author?lord?=?new?Author("Karen?Lord") {Website?=?"https://karenlord.wordpress.com/",RelatedAuthors?=?new() };lord.Books.AddRange(new?Book[]{new?Book("The?Best?of?All?Possible?Worlds",?2013,?lord),new?Book("The?Galaxy?Game",?2015,?lord)} );lord.RelatedAuthors.AddRange(new?Author[]{new?("Nalo?Hopkinson"),new?("Ursula?K.?Le?Guin"),new?("Orson?Scott?Card"),new?("Patrick?Rothfuss")} );Console.WriteLine($"Author:?{lord.Name}"); Console.WriteLine($"Books:?{lord.Books.Count}"); Console.WriteLine($"Related?authors:?{lord.RelatedAuthors.Count}");public?record?Author(string?Name) {private?List<Book>?_books?=?new();public?List<Book>?Books?=>?_books;public?string??Website?{get;?init;}public?string??Genre?{get;?init;}public?List<Author>??RelatedAuthors?{get;?init;} }public?record?Book(string?name,?int?Published,?Author?author);

這個程序編譯沒有出現(xiàn)警告。

你可能會對下面這句話感到疑惑:

lord.RelatedAuthors.AddRange(

Author.RelatedAuthors 可以為空,編譯器可以看到 RelatedAuthors 屬性是在前面幾行設(shè)置的,因此它知道 RelatedAuthors 引用是非空的。

但是,想象一下如果這個程序是這樣的:

Author?GetAuthor() {return?new?Author("Karen?Lord"){Website?=?"https://karenlord.wordpress.com/",RelatedAuthors?=?new()}; }Author?lord?=?GetAuthor();

當(dāng)類型構(gòu)造在一個單獨(dú)的方法中時,編譯器不能智能地知道 RelatedAuthors 是非空的。在這種情況下,將需要以下兩種模式之一:

lord.RelatedAuthors!.AddRange(

if?(lord.RelatedAuthors?is?object) {lord.RelatedAuthors.AddRange(?... }

這是一個關(guān)于記錄可空性的冗長演示,只是想說明它不會改變使用可空引用類型的任何體驗。

另外,您可能已經(jīng)注意到,我將 Author 記錄上的 Books 屬性改為一個初始化的 get-only 屬性,而不是記錄構(gòu)造函數(shù)中的一個必需參數(shù)。這是因為 Author 和 Books 之間存在一種循環(huán)關(guān)系(譯注:Author 含有List<Book>類型的導(dǎo)航屬性,Book 也包含 Author 類型的導(dǎo)航屬性)。不變性和循環(huán)引用可能會導(dǎo)致頭痛。在本例中,這是可以的,只是意味著需要在 Book 對象之前創(chuàng)建所有 Author 對象。因此,不可能在 Author 構(gòu)造中提供一組完全初始化好的 Book 對象作為 Author 構(gòu)建的一部分,我們所能期待的最好結(jié)果就是一個空的 List<Book>。因此,初始化一個作為 Author 構(gòu)建的一部分的空 List<Book> 似乎是最好的選擇。沒有規(guī)則規(guī)定所有這些屬性都必須是 init 的形式,我(示例中)之所以這樣做是為了示范。

我們將轉(zhuǎn)移到 JSON 序列化的話題。這個帶有循環(huán)引用的示例與稍后將在 JSON 對象圖部分中的保存引用有關(guān)。JsonSerializer 支持循環(huán)引用的對象圖,但不支持帶有參數(shù)化構(gòu)造函數(shù)的類型。你可以將 Author 對象序列化為 JSON,但不能將其反序列化為當(dāng)前定義的 Author 對象。如果 Author 不是記錄或者沒有循環(huán)引用,那么序列化和反序列化都可以使用 JsonSerializer。

System.Text.Json

System.Text.Json 在 .NET 5.0 中得到了顯著的改進(jìn),提高了性能和可靠性,并使熟悉 Newtonsoft.Json 的人更容易采用它。它還支持將 JSON 對象反序列化為記錄,這是本文之前的文章介紹過的 C# 新特性。

如果你想將 System.Text.Json 作為 Newtonsoft.Json 的替代品,可以看這個 遷移指南,該指南闡明了這兩者 API 之間的關(guān)系。System.Text.Json 旨在涵蓋與 Newtonsoft.Json 相同的大多數(shù)場景,但是它并不是用來替代該流行的 Json 庫的,也不是為了實現(xiàn)與流行的 Json 庫相同的功能。我們試圖在性能和可用性之間保持平衡,并在設(shè)計選擇中偏向于性能。

HttpClient 擴(kuò)展方法

JsonSerializer 擴(kuò)展方法現(xiàn)在公開到 HttpClient 上了,極大地簡化了同時使用這兩個 API。這些擴(kuò)展方法消除了復(fù)雜性,并為你處理各種場景,包括處理內(nèi)容流和驗證內(nèi)容媒體類型。Steve Gordon 很好地解釋了使用基于 System.Net.Http.Json 的 HttpClient 發(fā)送和接收 JSON 的好處。

下面的示例使用新的 GetFromJsonAsync<T>() 擴(kuò)展方法將天氣預(yù)報的 JSON 數(shù)據(jù)反序列化為 Forecast 記錄。

using?System; using?System.Net.Http; using?System.Net.Http.Json;string?serviceURL?=?"https://localhost:5001/WeatherForecast"; HttpClient?client?=?new(); Forecast[]?forecasts?=?await?client.GetFromJsonAsync<Forecast[]>(serviceURL);foreach(Forecast?forecast?in?forecasts) {Console.WriteLine($"{forecast.Date};?{forecast.TemperatureC}C;?{forecast.Summary}"); }//?{"date":"2020-09-06T11:31:01.923395-07:00","temperatureC":-1,"temperatureF":31,"summary":"Scorching"} public?record?Forecast(DateTime?Date,?int?TemperatureC,?int?TemperatureF,?string?Summary);

這段代碼非常緊湊!它依賴于來自 C# 9 的頂層程序和記錄,以及新的 GetFromJsonAsync<T>() 擴(kuò)展方法。如此近距離使用 foreach 和 await 可能會讓你懷疑我們是否會添加對 JSON 對象流的支持。是的,在未來的版本中。

譯注:上面作者所說的“近距離”我覺得意思是指反序列化時就近聲明需要的記錄類型,比單獨(dú)創(chuàng)建 Model 類放在單獨(dú)的文件中“近”許多。

你可以在你自己的機(jī)器上試試,下面的 .NET SDK 命令將使用 WebAPI 模板創(chuàng)建一個天氣預(yù)報服務(wù)。默認(rèn)情況下,它的服務(wù) URL 地址是:https://localhost:5001/WeatherForecast,與本示例中使用的 URL 相同。

rich@thundera?~?%?dotnet?new?webapi?-o?webapi rich@thundera?~?%?cd?webapi rich@thundera?webapi?%?dotnet?run

先確保你已經(jīng)運(yùn)行了 dotnet dev-certs https --trust,否則客戶端和服務(wù)器之間的將不能正常握手通訊。如果有問題,請參見 Trust the ASP.NET Core HTTPS development certificate.。

然后你可以運(yùn)行前面的例子:

rich@thundera?~?%?git?clone?https://gist.github.com/3b41d7496f2d8533b2d88896bd31e764.git?weather-forecast rich@thundera?~?%?cd?weather-forecast rich@thundera?weather-forecast?%?dotnet?run 9/9/2020?12:09:19?PM;?24C;?Chilly 9/10/2020?12:09:19?PM;?54C;?Mild 9/11/2020?12:09:19?PM;?-2C;?Hot 9/12/2020?12:09:19?PM;?24C;?Cool 9/13/2020?12:09:19?PM;?45C;?Balmy

改進(jìn)了對不可變類型的支持

定義不可變類型有多種模式,記錄只是最新的一種(比如下文示例中的一個 Struct 類型),JsonSerializer 現(xiàn)在支持不可變類型了。

在本例中,你將看到使用不可變結(jié)構(gòu)類型的序列化:

using?System; using?System.Text.Json; using?System.Text.Json.Serialization;var?json?=?"{\"date\":\"2020-09-06T11:31:01.923395-07:00\",\"temperatureC\":-1,\"temperatureF\":31,\"summary\":\"Scorching\"}?"; var?options?=?new?JsonSerializerOptions() {PropertyNameCaseInsensitive?=?true,IncludeFields?=?true,PropertyNamingPolicy?=?JsonNamingPolicy.CamelCase }; var?forecast?=?JsonSerializer.Deserialize<Forecast>(json,?options);Console.WriteLine(forecast.Date); Console.WriteLine(forecast.TemperatureC); Console.WriteLine(forecast.TemperatureF); Console.WriteLine(forecast.Summary);var?roundTrippedJson?=?JsonSerializer.Serialize<Forecast>(forecast,?options);Console.WriteLine(roundTrippedJson);public?struct?Forecast{public?DateTime?Date?{get;}public?int?TemperatureC?{get;}public?int?TemperatureF?{get;}public?string?Summary?{get;}[JsonConstructor]public?Forecast(DateTime?date,?int?temperatureC,?int?temperatureF,?string?summary)?=>?(Date,?TemperatureC,?TemperatureF,?Summary)?=?(date,?temperatureC,?temperatureF,?summary); }

注意:JsonConstructor 特性需要指定與 struct 一起使用的構(gòu)造函數(shù)。對于類,如果只有一個構(gòu)造函數(shù),那么該特性就不是必需的,記錄也是如此。

它的輸出如下:

rich@thundera?jsonserializerimmutabletypes?%?dotnet?run 9/6/2020?11:31:01?AM -1 31 Scorching {"date":"2020-09-06T11:31:01.923395-07:00","temperatureC":-1,"temperatureF":31,"summary":"Scorching"}

支持記錄

JsonSerializer 對記錄的支持幾乎與我剛才對不可變類型的支持相同。這里我想展示的不同之處是將一個 JSON 對象反序列化為一個記錄,該記錄公開一個參數(shù)化的構(gòu)造函數(shù)和一個可選的 init 屬性。

下面是一個包含了該記錄定義的程序:

using?System; using?System.Text.Json;Forecast?forecast?=?new(DateTime.Now,?40) {Summary?=?"Hot!" };string?forecastJson?=?JsonSerializer.Serialize<Forecast>(forecast); Console.WriteLine(forecastJson); Forecast??forecastObj?=?JsonSerializer.Deserialize<Forecast>(forecastJson); Console.Write(forecastObj);public?record?Forecast?(DateTime?Date,?int?TemperatureC) {public?string??Summary?{get;?init;} };

它的輸出如下:

rich@thundera?jsonserializerrecords?%?dotnet?run {"Date":"2020-09-12T18:24:47.053821-07:00","TemperatureC":40,"Summary":"Hot!"} Forecast?{?Date?=?9/12/2020?6:24:47?PM,?TemperatureC?=?40,?Summary?=?Hot!?}

改進(jìn)了 Dictionary<K,V> 的支持

JsonSerializer 現(xiàn)在支持具有非字符串鍵的字典。你可以在下面的示例中看到它的樣子。在 .NET Core 3.0 中,這段代碼可以編譯,但會拋出 NotSupportedException 異常。

using?System; using?System.Collections.Generic; using?System.Text.Json;Dictionary<int,?string>?numbers?=?new?() {{0,?"zero"},{1,?"one"},{2,?"two"},{3,?"three"},{5,?"five"},{8,?"eight"},{13,?"thirteen"},{21,?"twenty?one"},{34,?"thirty?four"},{55,?"fifty?five"}, };var?json?=?JsonSerializer.Serialize<Dictionary<int,?string>>(numbers);Console.WriteLine(json);var?dictionary?=?JsonSerializer.Deserialize<Dictionary<int,?string>>(json);Console.WriteLine(dictionary[55]);

它的輸出如下:

rich@thundera?jsondictionarykeys?%?dotnet?run {"0":"zero","1":"one","2":"two","3":"three","5":"five","8":"eight","13":"thirteen","21":"twenty?one","34":"thirty?four","55":"fifty?five"} fifty?five

支持字段

JsonSerializer 現(xiàn)在支持字段,這個變化是由 @YohDeadfall 貢獻(xiàn)的,感謝他!

你可以在下面的示例中看到它的樣子,在 .NET Core 3.0 中,JsonSerializer 無法對使用字段的類型進(jìn)行序列化或反序列化。對于具有字段且無法更改的現(xiàn)有類型來說,這是個問題,有了這個變化,這個問題就解決了。

using?System; using?System.Text.Json;var?json?=?"{\"date\":\"2020-09-06T11:31:01.923395-07:00\",\"temperatureC\":-1,\"temperatureF\":31,\"summary\":\"Scorching\"}?"; var?options?=?new?JsonSerializerOptions() {PropertyNameCaseInsensitive?=?true,IncludeFields?=?true,PropertyNamingPolicy?=?JsonNamingPolicy.CamelCase }; var?forecast?=?JsonSerializer.Deserialize<Forecast>(json,?options);Console.WriteLine(forecast.Date); Console.WriteLine(forecast.TemperatureC); Console.WriteLine(forecast.TemperatureF); Console.WriteLine(forecast.Summary);var?roundTrippedJson?=?JsonSerializer.Serialize<Forecast>(forecast,?options);Console.WriteLine(roundTrippedJson);public?class?Forecast{public?DateTime?Date;public?int?TemperatureC;public?int?TemperatureF;public?string?Summary; }

它的輸出如下:

rich@thundera?jsonserializerfields?%?dotnet?run 9/6/2020?11:31:01?AM -1 31 Scorching {"date":"2020-09-06T11:31:01.923395-07:00","temperatureC":-1,"temperatureF":31,"summary":"Scorching"}

保留 JSON 對象圖中的引用

JsonSerializer 增加了對在 JSON 對象圖中保留(循環(huán))引用的支持。它通過存儲在將 JSON 字符串反序列化回對象時可以重新構(gòu)建的 id 來實現(xiàn)這一點。

using?System; using?System.Collections.Generic; using?System.Text.Json; using?System.Text.Json.Serialization;Employee?janeEmployee?=?new() {Name?=?"Jane?Doe",YearsEmployed?=?10 };Employee?johnEmployee?=?new() {Name?=?"John?Smith" };janeEmployee.Reports?=?new?List<Employee>?{?johnEmployee?}; johnEmployee.Manager?=?janeEmployee;JsonSerializerOptions?options?=?new() {//?NEW:?globally?ignore?default?values?when?writing?null?or?defaultDefaultIgnoreCondition?=?JsonIgnoreCondition.WhenWritingDefault,//?NEW:?globally?allow?reading?and?writing?numbers?as?JSON?stringsNumberHandling?=?JsonNumberHandling.AllowReadingFromString?|?JsonNumberHandling.WriteAsString,//?NEW:?globally?support?preserving?object?references?when?(de)serializingReferenceHandler?=?ReferenceHandler.Preserve,IncludeFields?=?true,?//?NEW:?globally?include?fields?for?(de)serializationWriteIndented?=?true,};string?serialized?=?JsonSerializer.Serialize(janeEmployee,?options); Console.WriteLine($"Jane?serialized:?{serialized}");Employee?janeDeserialized?=?JsonSerializer.Deserialize<Employee>(serialized,?options); Console.Write("Whether?Jane's?first?report's?manager?is?Jane:?"); Console.WriteLine(janeDeserialized.Reports[0].Manager?==?janeDeserialized);public?class?Employee {//?NEW:?Allows?use?of?non-public?property?accessor.//?Can?also?be?used?to?include?fields?"per-field",?rather?than?globally?with?JsonSerializerOptions.[JsonInclude]public?string?Name?{?get;?internal?set;?}public?Employee?Manager?{?get;?set;?}public?List<Employee>?Reports;public?int?YearsEmployed?{?get;?set;?}//?NEW:?Always?include?when?(de)serializing?regardless?of?global?options[JsonIgnore(Condition?=?JsonIgnoreCondition.Never)]public?bool?IsManager?=>?Reports?.Count?>?0; }

性能

JsonSerializer 的性能在 .NET 5.0 中得到了顯著提高。Stephen Toub 在他的 .NET 5 的性能改進(jìn) 一文中介紹了一些 JsonSerializer 的改進(jìn),我將在這里再介紹一些。

集合的(反)序列化

我們對大型集合做了顯著的改進(jìn)(反序列化時為 1.15x-1.5x,序列化時為 1.5x-2.4x+)。你可以在 dotnet/runtime #2259 中更詳細(xì)地看到這些改進(jìn)。

與 .NET 5.0 和 .NET Core 3.1 相比,List<int> (反)序列化的改進(jìn)特別令人印象深刻,這些變化將在高性能應(yīng)用程序中體現(xiàn)出來。

屬性查找 —— 命名約定

使用 JSON 最常見的問題之一是命名約定與 .NET 設(shè)計準(zhǔn)則不匹配。JSON 屬性通常是 camelCase,.NET 屬性和字段通常是 PascalCase。你使用的 JSON 序列化器負(fù)責(zé)在命名約定之間架橋。這不是輕易就能做到的,至少對 .NET Core 3.1 來說不是。但在 .NET 5.0 中,這種實現(xiàn)成本現(xiàn)在可以忽略不計了。

允許缺少屬性和不區(qū)分大小寫的代碼在 .NET 5.0 中得到了極大的改進(jìn),在某些情況下它要快 1.75 倍。

下面是一個簡單的四屬性測試類的基準(zhǔn)測試,它的屬性名為大于 7 字節(jié)。

3.1?性能 |????????????????????????????Method?|???????Mean?|???Error?|??StdDev?|?????Median?|????????Min?|????????Max?|??Gen?0?|?Gen?1?|?Gen?2?|?Allocated?| |----------------------------------?|-----------:|--------:|--------:|-----------:|-----------:|-----------:|-------:|------:|------:|----------:| |?CaseSensitive_Matching????????????|???844.2?ns?|?4.25?ns?|?3.55?ns?|???844.2?ns?|???838.6?ns?|???850.6?ns?|?0.0342?|?????-?|?????-?|?????224?B?| |?CaseInsensitive_Matching??????????|???833.3?ns?|?3.84?ns?|?3.40?ns?|???832.6?ns?|???829.4?ns?|???841.1?ns?|?0.0504?|?????-?|?????-?|?????328?B?| |?CaseSensitive_NotMatching(Missing)|?1,007.7?ns?|?9.40?ns?|?8.79?ns?|?1,005.1?ns?|???997.3?ns?|?1,023.3?ns?|?0.0722?|?????-?|?????-?|?????464?B?| |?CaseInsensitive_NotMatching???????|?1,405.6?ns?|?8.35?ns?|?7.40?ns?|?1,405.1?ns?|?1,397.1?ns?|?1,423.6?ns?|?0.0626?|?????-?|?????-?|?????408?B?|5.0?性能 |????????????????????????????Method?|?????Mean?|???Error?|??StdDev?|???Median?|??????Min?|??????Max?|??Gen?0?|?Gen?1?|?Gen?2?|?Allocated?| |----------------------------------?|---------:|--------:|--------:|---------:|---------:|---------:|-------:|------:|------:|----------:| |?CaseSensitive_Matching????????????|?799.2?ns?|?4.59?ns?|?4.29?ns?|?801.0?ns?|?790.5?ns?|?803.9?ns?|?0.0985?|?????-?|?????-?|?????632?B?| |?CaseInsensitive_Matching??????????|?789.2?ns?|?6.62?ns?|?5.53?ns?|?790.3?ns?|?776.0?ns?|?794.4?ns?|?0.1004?|?????-?|?????-?|?????632?B?| |?CaseSensitive_NotMatching(Missing)|?479.9?ns?|?0.75?ns?|?0.59?ns?|?479.8?ns?|?479.1?ns?|?481.0?ns?|?0.0059?|?????-?|?????-?|??????40?B?| |?CaseInsensitive_NotMatching???????|?783.5?ns?|?3.26?ns?|?2.89?ns?|?783.5?ns?|?779.0?ns?|?789.2?ns?|?0.1004?|?????-?|?????-?|?????632?B?|

TechEmpower 改進(jìn)

譯注:TechEmpower 是一家主要做基準(zhǔn)測試的公司,它會定期提供各種 Web 應(yīng)用程序框架性能指標(biāo)的測試和比較,覆蓋了許多的語言框架,包括 C#,Go,Python,Java,Ruby,PHP 等。測試基于云和物理硬件,測試的性能則包括純文本響應(yīng)、序列化 JSON 對象、單個/多個數(shù)據(jù)庫查詢、數(shù)據(jù)庫更新、Fortunes 測試等等。

我們在 TechEmpower 基準(zhǔn)測試中花費(fèi)了大量的精力來改進(jìn) .NET 的性能。使用 TechEmpower JSON 基準(zhǔn)來驗證這些 JsonSerializer 改進(jìn)是有意義的。現(xiàn)在性能提高了約 19%,一旦我們將條目更新到 .NET 5.0 將提高 .NET 在基準(zhǔn)測試中的排行位置。這個版本的目標(biāo)是與 netty 相比更具競爭力,netty 是常見的 Java Webserver。

在 dotnet/runtime #37976 中詳細(xì)介紹了這些更改和性能度量。這里有兩套基準(zhǔn),第一個是使用團(tuán)隊維護(hù)的 JsonSerializer 性能基準(zhǔn)測試來驗證性能。觀察到有約 8% 的改善;第二個是 TechEmpower 的,它測量了滿足 TechEmpower JSON 基準(zhǔn)測試要求的三種不同方法。我們在官方基準(zhǔn)測試中使用的是SerializeWithCachedBufferAndWriter。

如果我們看一下 Min 列,我們可以做一些簡單的數(shù)學(xué)計算:153.3/128.6 = ~1.19,有了 19% 的提升。

結(jié)束

我希望你喜歡本文對記錄和 JsonSerializer 的深入介紹,它們只是 .NET 5.0 眾多改進(jìn)中的兩個。這篇預(yù)覽 8 的文章涵蓋了更多的新特性,這為 5.0 的價值提供了更廣闊的視角。

正如你所知道的,我們目前階段沒有在 .NET 5.0 中繼續(xù)添加新特性了。我利用后面的預(yù)覽和 RC 版本發(fā)布的文章來涵蓋我們已經(jīng)添加的所有功能的介紹。你希望我在 RC2 發(fā)布的博客文章中介紹哪些內(nèi)容?我想從你們那知道我應(yīng)該關(guān)注什么。

請在評論中分享你使用 RC1 的體驗,感謝所有安裝了 .NET 5.0 的人,我們感謝到目前為止我們收到的所有參與和反饋。

總結(jié)

以上是生活随笔為你收集整理的.NET 5.0 RC1 发布,离正式版发布仅剩两个版本,与 netty 相比更具竞争力的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。