共计 10839 个字符,预计需要花费 28 分钟才能阅读完成。
明天咱们公布了 .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 预览版。当初,让咱们理解一下此版本中的一些最新更新。
类型转换器
当初有针对新增加的原始类型
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";
};
示例:疏忽具备特定名称或类型的属性
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 属性的束缚的反对!
无关更多示例,请参阅束缚文档,无关新类型束缚的探讨,请退出模板引擎存储库中的探讨。
多选参数
预览版 6 还为 choice 参数增加了一项新性能——用户能够在单个抉择中指定多个值。这能够像应用 Flags-style 枚举一样应用。此类参数的常见示例可能是:
- 在 web 模板上抉择多种形式的身份验证
- 在 maui 模板中一次抉择多个指标平台(ios、android、web)
抉择退出此行为就像在模板配置中的参数定义中增加 “allowMultipleValues”:
true 一样简略。实现后,您将能够拜访许多用于模板内容的辅助函数,以帮忙检测用户抉择的特定值。
无关该性能的残缺阐明,请参阅多选参数文档。
退出代码对立和报告
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/run… 减少了对委托调用的爱护去虚拟化的反对。启用动静 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);
}
- 咱们开始实现冷热拆散,https://github.com/dotnet/run… 是它的第一局部。
ARM64 上的热 / 冷拆散已在 JIT (PR) 中实现。这项工作次要包含生成用于在热 / 冷局部之间分支的长伪指令,以及从数据局部加载常量。
咱们还增加了对带有异样解决 (PR) 的函数的热 / 冷拆分的反对。如果没有 PGO 数据,咱们的启发式算法会将所有异样处理函数挪动到冷段,并将“finally”块复制到热段;咱们是在异样很少产生的假如下运行的,然而无论是否存在异样,都会执行 finally 块。
在运行各种 SuperPMI 汇合时,JIT 将约 14% 的低端函数(无 PGO 数据)和约 26% 的高端函数(有 PGO 数据)拆分。在此处查看更多指标。
Arm64
- https://github.com/dotnet/run… 在 Windows Arm64 中启用了 LSE 原子。它将锁相干的操作性能进步了 78%。
- https://github.com/dotnet/run… 在 Arm64 上启用 gc 类型的寻址模式以取得高达 45% 的性能。
- https://github.com/dotnet/run… 对齐 16 字节 SIMD16 的 arm64 数据局部。
- https://github.com/dotnet/run… 优化 i % 2 并提供高达 17% 的吞吐量晋升。
循环优化
- 由类型测试驱动的循环克隆:
https ://github.com/dotnet/runtime/pull/70377 启用基于循环不变类型测试的循环克隆,例如 GDV 增加的那些。这无效地容许疾速门路循环将类型查看晋升到循环之外,从而进步性能。例如:
- 开始从 https://github.com/dotnet/run… 中的多级嵌套循环中晋升不变量。
个别优化
- PR https://github.com/dotnet/run… 改良了 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 7 中的重大更改文档找到最新的 .NET 7 重大更改列表。它按区域和版本列出了重大更改,并附有具体阐明的链接。
要查看提出了哪些重大更改但仍在审核中,请关注 Proposed .NET Breaking Changes GitHub issue。
路线图
.NET 版本包含产品、库、运行时和工具,代表了 Microsoft 内外多个团队之间的合作。您能够通过浏览产品路线图理解无关这些畛域的更多信息:
- ASP.NET Core 7 和 Blazor 路线图
- EF 7 路线图
- 机器学习网络
- .NET MAUI
- WinForms
- WPF
- NuGet
- Roslyn
- Runtime
完结
咱们感谢您对 .NET 的所有反对和奉献。请尝试 .NET 7 Preview 6 并通知咱们您的想法!
长按辨认二维码
关注微软中国 MSDN
点击下载 .NET 7 Preview 6 ~