主頁 > .NET開發 > 【翻譯】.NET 5 RC1發布

【翻譯】.NET 5 RC1發布

2020-09-15 16:25:12 .NET開發

9月14日,.NET5發布了(Release Candidate)RC1版本,RC的意思是指我們可以進行使用,并且RC版本得到了支持,該版本很接近.NET5.0的最終版本,也是11月正式版本之前兩個RC版本中的其中一個,目前,開發團隊正在尋找在.NET5發布之前剩余的bug,當然他們也希望我們的反饋以幫助他們順利的完成.NET5的開發計劃,

開發團隊在今天還發布了ASP.NET Core和EF Core的RC1版本,

現在我們可以進行下載用于Windows、macOS和Linux的.NET5

  • Installers and binaries
  • Container images
  • Snap installer
  • Release notes
  • Known issues
  • GitHub issue tracker

如果要使用.NET5,我們需要使用最新的Visual Studio預覽版(包括Visual Studio for Mac)

在.NET5中有許多的改進,特別是對單檔案可執行應用程式、更小的容器映像、更強大的JsonSerializer api、BCL nullable reference type annotated、新target framework names,以及對Windows ARM64的支持,在.NET庫中,GC和JIT的性能都得到了極大的提升,ARM64是性能優化的重點,它為我們帶來了更好的吞吐量和更小的二進制檔案,.NET5.0包含了新的語言版本,C#9和F#5.0,

下面還有他們最近發布的一些有關于.NET5.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

其實就像在.NET5 Preview8中一樣,在本章還是像上一章一樣選擇了一些特性來進行深入的研究介紹,在本章中將深入的討論C#9中新特性records System.Text.Json.JsonSerializer,它們是獨立的特性,但也是很好的一個組合,特別是在我們花費一些時間去為反序列化的JSON物件設計POCO型別時,

C# 9 — Records

Records可能是c#9中最重要的一個新特性,它們提供了一個廣泛的特性集(對于一種語言型別),其中一些需要RC1或更高的版本(如record.ToString()),

records看作不可變類是最簡單的方式,在特性方面,它們很接近元組(Tuple),可以將他們視為具有屬性和不可變性的自定義元組,在今天使用元組的許多情況下,records可以更好的提供這些元組,

如果你正在使用C#,你會得到最好的體驗,如果你使用命名型別(相對于像元組這樣的特性),靜態型別是該語言主要的設計要點,records使小型型別更容易使用,并在整個應用程式中利用型別安全,

Records are immutable data types

Records使我們能夠創建不可變的資料型別,這對于定義存盤少量資料的型別非常有用,

下面是一個records的示例,它存盤登錄用戶資訊.

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是一個新的關鍵字,它是set的代替,set允許我們在任何時候分配一個屬性,init只允許在物件構建期間進行屬性的賦值操作,它是records的不變性所依賴的基礎,任何型別都可以使用init,正如我們在前面的定義中所看到的那樣,它不是特定于records的,

private set看起來類似于init;private set防止其他代碼(型別以外的代碼)改變資料,當型別(在構建之后)意外的改變屬性時,init將在編譯器生成時回傳錯誤,private set并非旨在為不可變資料建模,因此當型別在構造后使屬性值發生沖突時,private set不會產生任何編輯器錯誤或者警告,

Records are specialized classes

正如上面提到的LoginResource的records的變數和類變數幾乎是相同的,類定義是記錄的一個語意相同的子集,records 提供了更多的、專門的行為,

下面是比較一個record和一個使用init而不是set作為屬性類之間的比較,

有什么相同?

  • Construction
  • Immutability
  • Copy semantics (records are classes under the hood)

有什么不同?

  • records相等性是基于內容的,基于物件標識的類相等性
  • records提供了一個GetHashCode()實作,它基于record內容
  • records提供一個IEquatable實作,它使用唯一的GetHashCode()行為作為機制,為record提供基于內容的相等語意,
  • 覆寫Record ToString()以列印record內容,

record和類(使用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結束,該模式并不是新的命名模式的規范,這樣命名只是為了我們在代碼片段中有相同型別的record和類變數,請不要這樣命名我們的型別,

如下是上面代碼的輸出內容

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

Record syntax

有多種用于宣告records的用例,在使用過每種方式后,我們就會對每一種模式的好處有所了解,我們還能看到不同方式,他們不是不同的語法而是多種選擇,

第一個方式是最簡單的,但是它的靈活性比較小,它適用于具有少量必需屬性的records

下面是前面顯示的LoginResource record,作為此模式的一個示例,這一行是的定義

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

構造遵循具有引數的建構式的要求(包括允許使用可選引數),

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

還可以使用目標型別,

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

下一個語法使所有屬性都是可選的,為record提供了一個隱式無引數建構式,

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

構造使用物件初始化器,看起來像下面這樣

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

如果我們想讓這兩個屬性是必須的,另一個是可選屬性,那么我們可以通過如下方式實作

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

構造可能如下所示,其中未指定RememberMe

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

如果說要指定RememberMe可以通過如下方式來實作

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

如果說我們不認為record只用于不可變資料,那么我們可以選擇公開可變屬性,如下代碼片段所示,該片段展示了關于電池的資訊,Model和TotalCapacityAmpHours屬性是不可變的,而剩余的容量百分比是可變的,

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

輸出結果如下所示:

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

Non-destructive record mutation

不變性是給我們帶來了很多的好處,但是我們也很快的發現了需要修改record的情況,在不放棄record的情況下,我們該如何處理這種情況呢?with運算式可以滿足這些需求,它可以根據相同型別的現有record來創建新record,我們可以指定想要的不同的新值,并從現有的record中復制所有其他屬性.

現在我們有個需求就是將用戶名轉換為小寫,這樣的情況下我們才可以將其保存到我們的資料庫中,如果說處理這個需求我們可能會像如下代碼片段中這樣去處理:

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

登錄record沒有被更改,事實上,這是不可能的,轉換只影響了loginLowercased,除了小寫轉換為loginLowercased之外其他與登錄相同,

我們可以使用內置的ToString()覆寫檢查with是否完成了預期的作業,

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

下面代碼是輸出

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

我們可以進一步的了解with的作業原理,它將所有的值從一條record復制到另一條record,這不是一個record依賴于另一個record的委托模型,事實上with操作完成后,兩個record之間就沒有關系了,只對record的構建有意義,這就意味著對于參考型別,副本只是參考副本,對于值型別,復制值.

您可以使用以下代碼查看該語意,

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}");

輸出:

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

Record inheritance

擴展record很容易,假設一個新的LastLoggedIn屬性,可以將其直接添加到LoginResource,record不像傳統的介面那樣脆弱,除非我們想創建需要建構式引數的新屬性.

這個新的record可以基于如下的LoginResource

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設定為一個必須的屬性,并且也增加了可選的屬性

Modeling record construction helpers

我們一起來看另一個例子,測量體重,體重的測量來自一個互聯網的秤,重量是以公斤來指定的,但是某些情況下,重點需要以磅來提供,

可以通過如下代碼片段進行宣告

public record WeightMeasurement(DateTime Date, int Kilograms)
{
    public int Pounds {get; init;}

    public static int GetPounds(int kilograms) => kilograms * 2.20462262;
}

這就是構造的樣子

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

在本例中,有必要將權重指定為local,不可能在物件初始化器中訪問公斤屬性,還需要將GetPounds定義為靜態方法,不可能在物件初始化器中呼叫實體方法(對于正在構造的型別),

Records and Nullability

一切都是不可變的,那么空值從何而來?不完全是,不可變屬性可以是null,并且在這種情況下將始終是null,

讓我們看看另一個沒有啟用可空性的程式,

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為空,

為了進一步說明這一點,將不編譯以下內容,author.Name 初始化為null,然后不能更改,因為屬性是不可變的,

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

下面啟動可空性

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net5.0</TargetFramework>
    <LangVersion>preview</LangVersion>
    <Nullable>enable</Nullable>
  </PropertyGroup>

</Project>

下面我們能看到一堆這樣的警告

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

用null注釋更新了Author record,這些注釋描述了我打算使用的record,

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

仍然得到了對null的警告,null構造的Author之前看到,

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

很好,因為我們想避免這種情況, 現在,下面展示該程式的更新版本,該版本可以很好地運行并享有可空性的好處,

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);

該程式在編譯時不會出現可空的警告,

大家可能對下面這句有疑惑

lord.RelatedAuthors.AddRange(

Author.RelatedAuthors可以為null, 編譯器可以看到,RelatedAuthors屬性的設定只是前面幾行,因此它知道RelatedAuthors參考將為非null,

但是,想象一下這個程式看起來是這樣的,

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

Author lord = GetAuthor();

編譯器沒有流程分析技巧,無法知道當型別構造在單獨的方法中時,RelatedAuthor將為非空, 在這種情況下,將需要以下兩種模式之一

lord.RelatedAuthors!.AddRange(

or

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

這是一個關于記錄可空性的冗長演示,只是為了說明它不會改變使用可空參考型別的任何體驗,

另外,您可能已經注意到,我將Author record上的Books屬性移動為初始化的get-only屬性,而不是記錄建構式中的必需引數, 這是由于作者與書籍之間存在回圈關系, 不變性和回圈參考可能會引起頭痛, 在這種情況下可以,并且僅表示需要在Book物件之前創建所有Author物件, 結果,無法提供完全初始化的Book物件集作為Author結構的一部分, 作為Author結構的一部分,我們可以期望的最好的是一個空的List , 結果,初始化空的List 作為Author結構的一部分似乎是最佳選擇, 沒有規則要求所有這些屬性都必須是init樣式,這樣做只是為了演示該行為,

我們將過渡到談論JSON序列化, 這個帶有回圈參考的示例與不久之后的在JSON物件圖中保存參考有關, JsonSerializer支持帶有回圈參考的物件圖,但不支持帶有引數化建構式的型別, 您可以將Author物件序列化為JSON,但不能序列化為當前定義的Author物件, 如果Author不是記錄或沒有回圈參考,那么JsonSerializer可以同時進行序列化和反序列化,

System.Text.Json

.NET 5.0中對System.Text.Json進行了顯著改進,以提高性能,可靠性,當然如果熟悉Newtonsoft.Json那么用起來更容易, 它還包括對將JSON物件反序列化為記錄的支持,本文前面已介紹了新的C#功能

如果要使用System.Text.Json替代Newtonsoft.Json,則應查看遷移指南, 該指南闡明了這兩個API之間的關系, System.Text.Json旨在涵蓋與Newtonsoft.Json相同的許多場景,但并不旨在替代流行的JSON庫或與流行的JSON庫實作功能對等, 我們嘗試在性能和可用性之間保持平衡,并在設計選擇中偏向性能,

HttpClient extension methods

JsonSerializer擴展方法現在在HttpClient上公開,并且極大地簡化了同時使用這兩個api,這些擴展方法消除了復雜性,并為您處理各種場景,包括處理內容流和驗證內容媒體型別,Steve Gordon很好地解釋了使用帶有System.Net.Http.Json的HttpClient發送和接收JSON的好處,

下面的示例使用新的GetFromJsonAsync()擴展方法將天氣預報JSON資料反序列化為預報記錄,

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的頂級程式和record,以及新的GetFromJsonAsync()擴展方法,在foreach和await的使用中可能大家會懷疑是否對流JSON物件的支持,在未來版本中是支持的,

大家可以在自己的機器上試試,下面的.NET SDK命令將使用WebAPI模板創建一個天氣預報服務,默認情況下,它將在以下URL公開服務:https://localhost:5001/WeatherForecast,這與示例中使用的URL相同,

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

確保已經運行dotnet dev-certs https——首先信任,否則客戶端和服務器之間的握手將不起作用,如果有問題,請參見信任ASP.NET Core HTTPS開發證書,

然后可以運行前面的示例,

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

Improved support for immutable types

其實定義不可變型別有多種方式,records只是最新的一種,JsonSerializer現在支持不可變型別

在下面示例中,我們將看到帶有不可變結構的序列化

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一起使用的建構式,對于類,如果只有一個建構式,那么屬性就不是必須的,與records相同,

輸出內容:

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

Support for records

JsonSerializer對records的支持與上面展示的不可變型別的支持幾乎相同,我想在這里顯示的區別是將JSON物件反序列化為一條records,該records公開了引數化的建構式和可選的init屬性,

在下面代碼片段中包含了對records的定義:

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

Improved Dictionary<K,V> support

JsonSerializer現在支持具有非字串鍵的字典,我們可以在下面的示例中看到它的樣子,在.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

Support for fields

JsonSerializer現在支持欄位,

我們可以在下面的示例中看到它的樣子,在.NET Core 3.0中,JsonSerializer無法對使用欄位的型別進行序列化或反序列化,對于具有欄位且無法更改的現有型別來說,這是一個問題,有了這個支持,這不再是一個問題,

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

Preserving references in JSON object graphs

JsonSerializer增加了對在JSON物件圖中保存(回圈)參考的支持,它通過存盤在將JSON字串反序列化回物件時可以重新構建的id來實作這一點,

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 default
    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault,
    // NEW: globally allow reading and writing numbers as JSON strings
    NumberHandling = JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString,
    // NEW: globally support preserving object references when (de)serializing
    ReferenceHandler = ReferenceHandler.Preserve,
    IncludeFields = true, // NEW: globally include fields for (de)serialization
    WriteIndented = 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;
}

Performance

在.NET 5.0中,JsonSerializer的性能得到了顯著改善, Stephen Toub在.NET 5中的Performance Improvements中涵蓋了JsonSerializer的一些改進, 我會在這里再介紹幾個,

Collections (de)serialization

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

將.NET 5.0與.NET Core 3.1進行比較,對List(反序列化)的改進特別令人印象深刻, 這些變化將在高性能應用程式中非常有意義,

Method Mean Error StdDev Median Min Max Gen 0 Gen 1 Gen 2 Allocated
Deserialize before 76.40 us 0.392 us 0.366 us 76.37 us 75.53 us 76.87 us 1.2169 8.25 KB
After ~1.5x faster 50.05 us 0.251 us 0.235 us 49.94 us 49.76 us 50.43 us 1.3922 8.62 KB
Serialize before 29.04 us 0.213 us 0.189 us 29.00 us 28.70 us 29.34 us 1.2620 8.07 KB
After ~2.4x faster 12.17 us 0.205 us 0.191 us 12.15 us 11.97 us 12.55 us 1.3187 8.34 KB

Property lookups — naming convention

使用JSON最常見的問題之一是命名規范與.NET設計準則不匹配,JSON屬性通常是camelCase, .NET屬性和欄位通常是PascalCase,我們使用的json序列化器負責在命名約定之間架橋,這不是免費的,至少對.NET Core 3.1來說不是,在.NET5中,這種成本現在可以忽略不計了,

.NET 5.0中大大改進了允許缺少屬性和不區分大小寫的代碼, 在某些情況下,速度快約1.75倍,

下面是一個簡單的4個屬性測驗類的基準測驗,它的屬性名為>7 bytes,

3.1 performance
|                            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 performance
|                            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 improvement

開發團隊在TechEmpower基準測驗中花費了大量的精力來改進.NET的性能,使用TechEmpower JSON基準來驗證這些JsonSerializer改進是很有意義的,現在性能提高了~ 19%,一旦我們將條目更新到.NET5,這將提高.NET5在基準測驗中的位置,這個版本的目標是與netty相比更具競爭力,netty是一種常見的Java web服務器,

在dotnet/runtime #37976中詳細介紹了這些更改和性能度量,這里有兩套基準,第一個是使用團隊維護的JsonSerializer性能基準測驗來驗證性能,觀察到有~8%的改善,下一部分是關于技術授權的,它測量了滿足TechEmpower JSON基準測驗要求的三種不同方法,SerializeWithCachedBufferAndWriter是我們在官方基準測驗中使用的

Method Mean Error StdDev Median Min Max Gen 0 Gen 1 Gen 2 Allocated
SerializeWithCachedBufferAndWriter (before) 155.3 ns 1.19 ns 1.11 ns 155.5 ns 153.3 ns 157.3 ns 0.0038 24 B
SerializeWithCachedBufferAndWriter (after) 130.8 ns 1.50 ns 1.40 ns 130.9 ns 128.6 ns 133.0 ns 0.0037 24 B

如果我們看一下Min列,我們可以做一些簡單的數學計算:153.3/128.6 = ~1.19,提高了19%,

Closing

本文對records和JsonSerializer有了一個更好的認識,它們只是.NET 5.0眾多改進中的兩個,preivew 8的文章涵蓋了更大的特性集,這為5.0的價值提供了更廣闊的視角,

正如我們所知道的,他們現在沒有在.NET 5.0中添加任何新特性,這些后期的預覽和RC的文章來涵蓋開發團隊已經建立的所有功能,當然大家可以在原文中進行留言,說一下在期望RC2中開發團隊這邊需要詳細介紹的特性,

原文:https://devblogs.microsoft.com/dotnet/announcing-net-5-0-rc-1/

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/48231.html

標籤:.NET Core

上一篇:NPOI使用手冊

下一篇:ASP.NET MVC 中解決api請求引數為空字串時后臺接收為null的問題

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more