宣佈 .NET 7 預覽版 6

語言: CN / TW / HK

今天我們發佈了 .NET 7 預覽版 6。.NET 7 的這個預覽版包括對類型轉換器的改進、JSON 合約可自定義、System.Formats.Tar API 更新、對 .NET 模板創作的約束以及 CodeGen 領域的性能增強。

您可以下載適用於 Windows、macOS 和 Linux 的.NET 7 Preview 6。

  • 安裝程序和二進制文件

  • 容器圖像

  • Linux 軟件包

  • 發行説明

  • 已知的問題

  • GitHub 問題跟蹤器

.NET 7 Preview 6 已在 Visual Studio 17.3 Preview 3 上完成測試。如果您想在 Visual Studio 系列產品中試用 .NET 7,我們建議您使用預覽通道版本。如果您使用的是 macOS,我們建議使用最新的Visual Studio 2022 for Mac 預覽版。現在,讓我們瞭解一下此版本中的一些最新更新。

  • .NET 7 Preview 6 :

    https://dotnet.microsoft.com/zh-cn/download/dotnet/7.0?ocid=AID3045631

  • 安裝程序和二進制文件:

    https://dotnet.microsoft.com/download/dotnet/7.0?ocid=AID3045631

  • 容器圖像:

    https://mcr.microsoft.com/catalog?search=dotnet/

  • Linux 軟件包:

    https://github.com/dotnet/core/blob/master/release-notes/7.0/

  • 發行説明:

    https://github.com/dotnet/core/tree/master/release-notes/7.0

  • 已知的問題:

    https://github.com/dotnet/core/blob/main/release-notes/7.0/known-issues.md

  • GitHub 問題跟蹤器:

    https://github.com/dotnet/core/issues

  • 預覽通道版本:

    https://visualstudio.microsoft.com/vs/preview/

  • Visual Studio 2022 for Mac 預覽版:

    https://visualstudio.microsoft.com/vs/mac/preview/

類型轉換器

現在有針對新添加的原始類型 DateOnly TimeOnly Int128 、UInt128 和 Half 的公開類型轉換器。

namespace System.ComponentModel
{
public class DateOnlyConverter : System.ComponentModel.TypeConverter
{
public DateOnlyConverter() { }
}


public class TimeOnlyConverter : System.ComponentModel.TypeConverter
{
public TimeOnlyConverter() { }
}


public class Int128Converter : System.ComponentModel.BaseNumberConverter
{
public Int128Converter() { }
}


public class UInt128Converter : System.ComponentModel.BaseNumberConverter
{
public UInt128Converter() { }
}


public class HalfConverter : System.ComponentModel.BaseNumberConverter
{
public HalfConverter() { }
}
}

使用示例

TypeConverter dateOnlyConverter = TypeDescriptor.GetConverter(typeof(DateOnly));
// 產生 DateOnly(1940, 10, 9) 的 DateOnly 值
DateOnly? date = dateOnlyConverter.ConvertFromString("1940-10-09") as DateOnly?;


TypeConverter timeOnlyConverter = TypeDescriptor.GetConverter(typeof(TimeOnly));
// 產生 TimeOnly(20, 30, 50) 的 TimeOnly 值
TimeOnly? time = timeOnlyConverter.ConvertFromString("20:30:50") as TimeOnly?;


TypeConverter halfConverter = TypeDescriptor.GetConverter(typeof(Half));
// 產生 -1.2 的一半值
Half? half = halfConverter.ConvertFromString(((Half)(-1.2)).ToString()) as Half?;


TypeConverter Int128Converter = TypeDescriptor.GetConverter(typeof(Int128));
// 產生 Int128 的 Int128 值。最大值 等於 170141183460469231731687303715884105727
Int128? int128 = Int128Converter.ConvertFromString("170141183460469231731687303715884105727") as Int128?;


TypeConverter UInt128Converter = TypeDescriptor.GetConverter(typeof(UInt128));
// 產生 UInt128 的 UInt128 值。最大值 等於 340282366920938463463374607431768211455
UInt128? uint128 = UInt128Converter.ConvertFromString("340282366920938463463374607431768211455") as UInt128?;

JSON合約定製

在某些情況下,序列化或反序列化 JSON 的開發人員發現他們不想或不能更改類型,因為它們要麼來自外部庫,要麼會因為需要進行一些影響序列化的更改而嚴重污染代碼例如刪除屬性、更改數字的序列化方式,以及對象的創建方式。開發人員經常被迫編寫包裝器或自定義轉換器,這不僅很麻煩,而且會使序列化變慢。

JSON 合約可自定義允許用户更好地控制類型序列化或反序列化的內容和方式。

選擇定製

開發人員可以通過兩種基本方式“插入”自定義,它們最終都會分配 JsonSerializerOptions.TypeInfoResolver 並需要分配解析器:

  • 開發者可以使用 DefaultJsonTypeInfoResolver 並添加其修飾符,所有修飾符將被串行調用:

JsonSerializerOptions options = new()
{
TypeInfoResolver = new DefaultJsonTypeInfoResolver()
{
Modifiers =
{
(JsonTypeInfo jsonTypeInfo) =>
{
// 您在此處的修改,即:
if (jsonTypeInfo.Type == typeof(int))
{
jsonTypeInfo.NumberHandling = JsonNumberHandling.AllowReadingFromString;
}
}
}
}
};
Point point = JsonSerializer.Deserialize<Point>(@"{""X"":""12"",""Y"":""3""}", options);
Console.WriteLine($"({point.X},{point.Y})"); // (12,3)
public class Point
{
public int X { get; set; }
public int Y { get; set; }
}
  • 通過實現 System.Text.Json.Serialization.Metadata.IJsonTypeInfoResolver 編寫自己的自定義解析器。

  • 未處理類型時,代碼應返回 null

  • IJsonTypeInfoResolver 可以與其他內容組合成有效的解析器,它將返回第一個非空答案。

    例如 JsonTypeInfoResolver.Combine(new MyResolver(), new DefaultJsonTypeInfoResolver())

自定義

IJsonTypeInfoResolver 的工作是為任何類型序列化器請求提供 JsonTypeInfo  -每個選項每個 類型 只會發生一次。 JsonTypeInfo.Kind 將確定開發人員可以更改哪些旋鈕並根據轉換器確定,該轉換器基於提供給選項的轉換器確定。例如 JsonTypeInfoKind.Object 意味着可以添加/修改 屬性 ,而 JsonTypeInfoKind.None 意味着不能保證使用任何旋鈕——當類型具有自定義轉換器時可能會發生這種情況。

JsonTypeInfo 可以由 DefaultJsonTypeInfoResolver 創建,帶有來自即自定義屬性的預填充旋鈕,或者可以由用户從頭開始創建: JsonTypeInfo.CreateJsonTypeInfo - 從頭開始創建意味着用户還需要設置 JsonTypeInfo.CreateObject

自定義屬性

僅當 JsonTypeInfo.Kind == JsonTypeInfoKind.Object DefaultJsonTypeInfoResolver 將被預填充時,屬性才相關。它們可以通過使用 JsonTypeInfo.CreateJsonPropertyInfo 修改或創建並添加到屬性列表中,即假設您從單獨的庫中獲得了一個類,該類具有您無法更改的奇怪設計的 API:

class MyClass
{
private string _name = string.Empty;
public string LastName { get; set; }


public string GetName() => _name;
public void SetName(string name)
{
_name = name;
}
}

在此功能存在之前,您需要包裝類型層次結構或為該類型創建自己的自定義轉換器。現在您可以簡單地修復它:

JsonSerializerOptions options = new()
{
TypeInfoResolver = new DefaultJsonTypeInfoResolver()
{
Modifiers = { ModifyTypeInfo }
}
};


MyClass obj = new()
{
LastName = "Doe"
};


obj.SetName("John");


string serialized = JsonSerializer.Serialize(obj, options); // {"LastName":"Doe","Name":"John"}


static void ModifyTypeInfo(JsonTypeInfo ti)
{
if (ti.Type != typeof(MyClass))
return;


JsonPropertyInfo property = ti.CreateJsonPropertyInfo(typeof(string), "Name");
property.Get = (obj) =>
{
MyClass myClass = (MyClass)obj;
return myClass.GetName();
};


property.Set = (obj, val) =>
{
MyClass myClass = (MyClass)obj;
string value = (string)val;
myClass.SetName(value);
};


ti.Properties.Add(property);
}

屬性的條件序列化

在某些使用場景中,要求某些默認值不被序列化。例如某些時候您不希望 0 出現在 JSON 中的某些屬性裏。之前可以通過將 JsonIgnoreAttribute JsonIgnoreCondition.WhenWritingDefault 一起使用來使該場景正常工作。但是當您的默認值為非0例如-1時或者當您的默認值取決於外部設置時,就會出現問題。

現在您可以使用您想要的任何條件設置您自己的謂詞 ShouldSerialize 。例如您設置了一個 字符串 屬性並且您希望 N/A 不會出現在 JSON 中:

// 您要自定義的字符串屬性
JsonPropertyInfo property = ...;


property.ShouldSerialize = (obj, val) =>
{
// 在這個特定的例子中,我們不使用 parent 但如果需要它是可用的
MyClass parentObj = (MyClass)obj;
string value = (string)val;
return value != "N/A";
};
  • JsonIgnoreAttribute:

    https://docs.microsoft.com/zh-cn/dotnet/api/system.text.json.serialization.jsonignoreattribute?view=net-6.0 ?ocid=AID3045631

示例:忽略具有特定名稱或類型的屬性

var modifier = new IgnorePropertiesWithNameOrType();
modifier.IgnorePropertyWithType(typeof(SecretHolder));
modifier.IgnorePropertyWithName("IrrelevantDetail");
JsonSerializerOptions options = new()
{
TypeInfoResolver = new DefaultJsonTypeInfoResolver()
{
Modifiers = { modifier.ModifyTypeInfo }
}
};
ExampleClass obj = new()
{
Name = "Test",
Secret = new SecretHolder() { Value = "MySecret" },
IrrelevantDetail = 15,
};
string output = JsonSerializer.Serialize(obj, options); // {"Name":"Test"}
class ExampleClass
{
public string Name { get; set; }
public SecretHolder Secret { get; set; }
public int IrrelevantDetail { get; set; }
}
class SecretHolder
{
public string Value { get; set; }
}
class IgnorePropertiesWithNameOrType
{
private List<Type> _ignoredTypes = new List<Type>();
private List<string> _ignoredNames = new List<string>();
public void IgnorePropertyWithType(Type type)
{
_ignoredTypes.Add(type);
}
public void IgnorePropertyWithName(string name)
{
_ignoredNames.Add(name);
}
public void ModifyTypeInfo(JsonTypeInfo ti)
{
JsonPropertyInfo[] props = ti.Properties.Where((pi) => !_ignoredTypes.Contains(pi.PropertyType) && !_ignoredNames.Contains(pi.Name)).ToArray();
ti.Properties.Clear();
foreach (var pi in props)
{
ti.Properties.Add(pi);
}
}
}

System.Formats.Tar API 更新

在 Preview 4 中,引入了 System.Formats.Tar 程序集。它提供了用於操作 TAR 檔案的 API。

在 Preview 6 中,進行了一些更改以涵蓋一些特殊情況:

全局擴展屬性專用類

最初的設計假設只有 PAX TAR 檔案可以在第一個位置包含單個全局擴展屬性 (GEA) 條目,但發現 TAR 檔案可以包含多個 GEA 條目,這會影響所有後續條目,直到遇到新的 GEA條目或存檔的結尾。

還發現 GEA 條目不應僅出現在僅包含 PAX 條目的檔案中:它們可以出現在混合不同格式條目的檔案中。因此添加了一個新類來描述 GEA 條目:

+ public sealed partial class PaxGlobalExtendedAttributesTarEntry : PosixTarEntry
+ {
+ public PaxGlobalExtendedAttributesTarEntry(IEnumerable<KeyValuePair<string, string>> globalExtendedAttributes) { }
+ public IReadOnlyDictionary<string, string> GlobalExtendedAttributes { get { throw null; } }
+ }

條目格式,而非存檔格式

由於還發現不同格式的條目可以混合在一個 TAR 存檔中,因此將  TarFormat 枚舉重命名為 TarEntryFormat : 

-public enum TarFormat
+public enum TarEntryFormat
{
...
}

併為 TarEntry 添加了一個新屬性以公開條目的格式:

public abstract partial class TarEntry
{
...
+ public TarEntryFormat Format { get { throw null; } }
...
}

寫作和閲讀的變化

Format 屬性已從 TarReader 中刪除,因為不希望任何存檔都具有單一格式的所有條目。

由於 GEA 條目現在使用它們自己的專用類進行描述,並且可以在單個存檔中找到這種類型的多個條目,因此 TarReader 的字典屬性也被刪除:

public sealed partial class TarReader : IDisposable
{
...
- public TarFormat Format { get { throw null; } }
- public IReadOnlyDictionary<string, string>? GlobalExtendedAttributes { get { throw null; } }
...
}

對專門的 GEA 類的補充也對 TarWriter產生了影響

  • 刪除了曾用於獲取單個首位 GEA 條目的字典的構造函數。

  • 添加了一個只接受流和 leaveOpen 布爾值的新構造函數。

  • 保留了採用 TarFormat 的構造函數,但重命名了枚舉,並將默認值設置為 Pax 。該方法的文檔已更改,以解釋指定的格式參數僅適用於從文件添加條目的 TarWriter.WriteEntry 方法。

public sealed partial class TarWriter : IDisposable
{
...
- public TarWriter(Stream archiveStream, IEnumerable<KeyValuePair<string, string>>? globalExtendedAttributes = null, bool leaveOpen = false) { }
+ public TarWriter(Stream archiveStream, bool leaveOpen = false) { }
- public TarWriter(Stream archiveStream, TarFormat archiveFormat, bool leaveOpen = false) { }
+ public TarWriter(Stream archiveStream, TarEntryFormat format = TarEntryFormat.Pax, bool leaveOpen = false) { }
public void WriteEntry(string fileName, string? entryName) { }
...
}

模板創作

約束

預覽版 6向 .NET 模板引入了約束的概念。約束允許您定義允許模板的上下文——這可以幫助模板引擎確定它應該在 dotnet new list 等命令中顯示哪些模板。對於此版本,我們添加了對三種約束的支持:

  • 操作系統——根據用户的操作系統限制模板

  • 模板引擎主機——它根據哪個主機執行模板引擎來限制模板——這通常是 .NET CLI 自身,或者像 Visual Studio/Visual Studio for Mac 中的新項目對話框這樣的嵌入式場景

  • 已安裝的工作負載 – 要求在模板可用之前安裝指定的 .NET SDK 工作負載

在所有情況下,描述這些約束就像在模板的配置文件中添加一個新的 約束 部分一樣簡單:

"constraints": {
"web-assembly": {
"type": "workload",
"args": "wasm-tools"
},
}

這些模板可以命名,我們將在通知用户為什麼他們不能調用您的模板時使用該名稱。

目前,.NET CLI 支持這些約束,我們正在與 Visual Studio 團隊中的合作伙伴合作,將它們整合到您已經知道的項目和項目創建體驗中。

我們希望此功能將為 SDK 用户帶來更一致的體驗,無論他們選擇何種編輯器,更容易引導用户瞭解必要的模板先決條件,並幫助我們整理常見場景的模板列表,例如 dotnet 新列表 。在 .NET 7 的未來預覽版中,我們計劃添加對基於通用 MSBuild 屬性的約束的支持!

有關更多示例,請參閲約束文檔,有關新類型約束的討論,請加入模板引擎存儲庫中的討論。

  • 約束文檔:

    https://github.com/dotnet/templating/blob/main/docs/Constraints.md

  • 討論:

    https://github.com/dotnet/templating/discussions/4936

多選參數

預覽版 6 還為 choice 參數添加了一項新功能——用户可以在單個選擇中指定多個值。這可以像使用 Flags -style 枚舉一樣使用。此類參數的常見示例可能是:

  • web 模板上選擇多種形式的身份驗證

  • maui 模板中一次選擇多個目標平台(ios、android、web)

選擇加入此行為就像在模板配置中的參數定義中添加 "allowMultipleValues": true 一樣簡單。完成後,您將可以訪問許多用於模板內容的輔助函數,以幫助檢測用户選擇的特定值。

有關該功能的完整説明,請參閲多選參數文檔。

  • 多選參數文檔:

    https://github.com/dotnet/templating/blob/main/docs/Reference-for-template.json.md#multichoice-symbols-specifics

退 出代碼統一和報告

Preview 6 還統一了模板引擎報告的退出代碼。這應該能幫助那些在自己選擇的shell中依賴腳本的用户獲得更一致的錯誤處理體驗。此外,.NET CLI 報告的錯誤現在包含一個鏈接,可用於查找有關每個退出代碼的詳細信息:

➜ dotnet new unknown-template
No templates found matching: 'unknown-template'.


To list installed templates, run:
dotnet new list
To search for the templates on NuGet.org, run:
dotnet new search unknown-template


For details on the exit code, refer to https://aka.ms/templating-exit-codes#103

CodeGen

動態 PGO

  • https://github.com/dotnet/runtime/pull/68703 增加了對委託調用的保護去虛擬化的支持。啟用動態 PGO 後,當 JIT 確定這可能有利可圖時,它允許 JIT 專門化和內聯委託調用。這可以大大提高性能,如下面的微基準所示,其中動態 PGO 現在比沒有 PGO 快大約 5 倍(之前大約是 2.5 倍)。

目前只支持綁定到實例方法的委託。我們預計對靜態方法的支持將出現在 .NET 8 的早期預覽版中。

public class Benchmark
{
private readonly long[] _nums;
public Benchmark()
{
_nums = Enumerable.Range(0, 100000).Select(i => (long)i).ToArray();
}


[Benchmark]
public long Sum() => _nums.Sum(l => l * l);
}
方法 工作 工具鏈 中位數 錯誤 標準差

比率差

‍Sum Job-QWNDLL \nopgo\corerun.exe 406.65 us 0.718 us 0.560 us 1.00
Sum Job-PNPEDU \tieredpgo_no_delegate_gdv\corerun.exe 172.77 us 0.819 us 0.766 us 0.42
Sum Job-KFFWQK \tieredpgo_delegate_gdv\corerun.exe 91.38 us 0.263 us 0.219 us 0.22
  • 我們開始實現冷熱分離, https://github.com/dotnet/runtime/pull/69763   是它的第一部分。

  • ARM64 上的熱/冷分離已在 JIT (PR) 中實現。這項工作主要包括生成用於在熱/冷部分之間分支的長偽指令,以及從數據部分加載常量。

  • 我們還添加了對帶有異常處理 (PR) 的函數的熱/冷拆分的支持。如果沒有 PGO 數據,我們的啟發式算法會將所有異常處理函數移動到冷段,並將“finally”塊複製到熱段;我們是在異常很少發生的假設下運行的,但是無論是否存在異常,都會執行 finally 塊。

  • 在運行各種 SuperPMI 集合時,JIT 將約 14% 的低端函數(無 PGO 數據)和約 26% 的高端函數(有 PGO 數據)拆分。在此處查看更多指標。

  • J IT (PR):

    https://github.com/dotnet/runtime/pull/70708

  • PR:

    https://github.com/dotnet/runtime/pull/71236

  • 此處:

    https://github.com/dotnet/runtimelab/pull/1923#issuecomment-1172558978

Arm64

  • https://github.com/dotnet/runtime/pull/70600   在 Windows Arm64 中啟用了 LSE 原子。它將鎖相關的操作性能提高了 78%。

  • https://github.com/dotnet/runtime/pull/70749   在 Arm64 上啟用 gc 類型的尋址模式以獲得高達 45% 的性能。

  • https://github.com/dotnet/runtime/pull/71044   對齊 16 字節 SIMD16 的 arm64 數據部分。

  • https://github.com/dotnet/runtime/pull/70599   優化 i % 2 並提供高達 17% 的吞吐量提升。

循環優化

  • 由類型測試驅動的循環克隆:

    https ://github.com/dotnet/runtime/pull/70377    啟用基於循環不變類型測試的循環克隆,例如 GDV 添加的那些。這有效地允許快速路徑循環將類型檢查提升到循環之外,從而提高性能。例如:

  • 開始從 https://github.com/dotnet/runtime/pull/68061 中的多級嵌套循環中提升不變量。

一般優化

  • PR https://github.com/dotnet/runtime/pull/68874   改進了 JIT 中向量常量的處理,包括對值編號、常量傳播和其他常量已經可用的其他優化的支持。

面向 .NET 7

要面向 .NET 7,您需要在項目文件中使用 .NET 7 Target Framework Moniker (TFM)。例如:

<TargetFramework>net7.0</TargetFramework>

全套 .NET 7 TFM,包括特定於操作的 TFM。

  • net7.0

  • net7.0-android

  • net7.0-ios

  • net7.0-maccatalyst

  • net7.0-macos

  • net7.0-tvos

  • net7.0-windows

我們希望從 .NET 6 升級到 .NET 7 應該很簡單。請報告您在使用 .NET 7 測試現有應用程序的過程中發現的任何重大更改。

支持

.NET 7 是一個短期支持 (STS)版本,這意味着它將在發佈之日起 18 個月內獲得免費支持和補丁。請務必注意,所有版本的質量都是相同的。唯一的區別是支撐的長度。有關 .NET 支持政策的更多信息,請參閲.NET 和 .NET Core 官方支持政策。

我們最近將“當前”名稱更改為“短期支持 (STS)”。我們正在推出這一改變

  • .NET 和 .NET Core 官方支持政策:

    https://dotnet.microsoft.com/zh-cn/platform/support/policy/dotnet-core?ocid=AID3045631

  • 推出這一改變:

    https://github.com/dotnet/core/pull/7517

重大變化

您可以通過閲讀 .NET 7 中的重大更改文檔找到最新的 .NET 7 重大更改列表。它按區域和版本列出了重大更改,並附有詳細説明的鏈接。

要查看提出了哪些重大更改但仍在審核中,請關注Proposed .NET Breaking Changes GitHub issue。

  • 最新的 .NET 7 重大更改列表:

    https://docs.microsoft.com/zh-cn/dotnet/core/compatibility/7.0?ocid=AID3045631

  • Proposed .NET Breaking Changes GitHub issue:

    https://github.com/dotnet/core/issues/7131

路線圖

.NET 版本包括產品、庫、運行時和工具,代表了 Microsoft 內外多個團隊之間的協作。您可以通過閲讀產品路線圖瞭解有關這些領域的更多信息:

  • ASP.NET Core 7 和 Blazor 路線圖:

    https://github.com/dotnet/aspnetcore/issues/39504

  • EF 7 路線圖:

    https://docs.microsoft.com/ef/core/what-is-new/ef-core-7.0/plan?ocid=AID3045631

  • 機器學習網絡:

    https://github.com/dotnet/machinelearning/blob/main/ROADMAP.md

  • .NET MAUI:

    https://github.com/dotnet/maui/wiki/Roadmap

  • WinForms:

    https://github.com/dotnet/winforms/blob/main/docs/roadmap.md

  • WPF:

    https://github.com/dotnet/wpf/blob/main/roadmap.md

  • NuGet:

    https://github.com/NuGet/Home/issues/11571

  • Roslyn:

    https://github.com/dotnet/roslyn/blob/main/docs/Language Feature Status.md

  • Runtime:

    https://github.com/dotnet/core/blob/main/roadmap.md

結束

我們感謝您對 .NET 的所有支持和貢獻。請嘗試 .NET 7 Preview 6並告訴我們您的想法!

  • 感謝您:

    https://dotnet.microsoft.com/en-us/thanks

  • 嘗試 .NET 7 Preview 6:

    https://dotnet.microsoft.com/zh-cn/download/dotnet/7.0?ocid=AID3045631

謝謝你讀完了本文~相信你一定有一些感想、觀點、問題想要表達。 歡迎在評論區暢所欲言 ,期待聽到你的“聲音”哦!

同時, 喜歡的內容也不要忘記轉發給你的小夥伴們 ,謝謝你的支持!

長按識別二維碼

關注微軟中國MSDN

點擊「閲讀原文」下載 .NET 7 Preview 6   ~