在这篇文章中,我将简要提及 .NET 8 的新功能和变化。
dotnet publish和dotnet pack发布模式
使用此新版本,dotnet publish 和 dotnet pack 命令将以 Release 模式进行构建和打包。在此之前,它是在 Debug 模式下生成的。为了能够在 Debug 模式下生成,您需要将此参数 -p:PublishRelease 设置为 false。
dotnet publish -> /app/bin/Release/net8.0/app.dll
dotnet publish -p:PublishRelease=false -> /app/bin/Debug/net8.0/app.dll
System.Text.Json序列化
在最近的版本中,System.Text.Json 替换了 Newtonsoft.Json。我们现在也在 ABP 框架中使用 System.Text.Json。对象序列化和反序列化有多项增强功能。
最新版本的源代码生成器在与 ASP.NET Core 一起使用时,为 Native AOT 应用程序提供了改进的性能和可靠性。它还允许使用基于反射的序列化中已支持的 required 属性和 init 属性对类型进行序列化。此外,现在还有一个选项,可以自定义处理 JSON 有效负载中不存在的成员,请参阅
https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/missing-members。
支持对接口层次结构中的属性进行序列化。JsonNamingPolicy 功能已扩展到包括用于 snake_case 和 kebab-case 属性名称转换的新命名策略。最后,
JsonSerializerOptions.MakeReadOnly 方法允许显示控制 JsonSerializerOptions 实例何时被冻结,您可以使用 IsReadOnly 属性检查其状态。
随机性
如今,人工智能编程非常流行。 并且产生了生产更多随机内容的需求。
GetItems()
引入了两种新方法:Random.GetItems 和
RandomNumberGenerator.GetItems,使开发人员能够从给定的输入集中随机选择一定数量的项目。 下面的示例演示了如何使用 System.Random.GetItems
private static ReadOnlySpan countries = new[]
{
new CountryPhoneCode("Turkey", "90"),
new CountryPhoneCode("China", "86"),
new CountryPhoneCode("Germany", "49"),
new CountryPhoneCode("Finland", "358"),
new CountryPhoneCode("Spain", "34")
};
var randomValues = Random.Shared.GetItems(countries, 2);
foreach (var x in randomValues)
{
Console.WriteLine(x.Name + " -> " + x.CountryPhoneCode);
}
/**************
- Output -
Germany -> 49
Finland -> 358
**************/
Shuffle()
如果您需要在应用程序中随机化一个范围的顺序,您可以利用两种新方法:Random.Shuffle 和
RandomNumberGenerator.Shuffle。当您希望通过改变训练和测试数据的呈现顺序来最大程度地减少机器学习中训练偏差的影响时,这些方法特别方便。 使用这些方法,您可以确保数据集中的第一项仅有时用于训练,最后一项仅有时保留用于测试。
var trainingData = GetData();
Random.Shared.Shuffle(trainingData);
IDataView source = mlContext.Data.LoadFromEnumerable(trainingData);
DataOperationsCatalog.TrainTestData splittedData = mlContext.Data.TrainTestSplit(source);
model = chain.Fit(splittedData.TrainSet);
IDataView resultPredictions = model.Transform(split.TestSet);
性能改进
- 在 .NET 8 中,引入了各种新类型来提升应用程序性能。.NET 8 中的 System.Collections.Frozen 命名空间包括 FrozenDictionary 和 FrozenSet 集合类型。这些类型旨在防止在创建集合后对键和值进行更改,从而实现更快的读取操作,如 TryGetValue()。它们特别适用于在首次使用时填充的集合,然后持久化用于长期运行的服务。
private static readonly FrozenDictionary frozenData = LoadConfigurationData().ToFrozenDictionary(optimizeForReads: true);
//////
if (frozenData.TryGetValue(key, out bool setting) && setting)
{
Process();
}
- Buffers.IndexOfAnyValues 是 .NET 8 中的一种新类型,旨在传递给搜索传递集合中任何值的第一个出现的方法。像 String.IndexOfAny 和 MemoryExtensions.IndexOfAny 这样的方法的新重载接受新类型的实例。当您创建 Buffers.IndexOfAnyValues 的实例时,所有优化后续搜索所需的数据都会在那个时候派生出来。
- Text.CompositeFormat 是 .NET 8 中的一种新类型,用于优化在编译时未知的格式字符串(例如从资源文件加载的格式字符串)。虽然会在前期花费一些额外时间来执行诸如解析字符串之类的任务,但它可以避免每次使用格式字符串时都进行这些工作。
private static readonly CompositeFormat range = CompositeFormat.Parse(Load());
//////////
static string GetMessage(int min, int max) =>
string.Format(CultureInfo.InvariantCulture, range, min, max);
- 在 .NET 8 中,引入了两种新类型来实现快速的 XxHash3 和 XxHash128 哈希算法。
System.Numerics和System.Runtime.Intrinsics的改进
对于 System.Numerics 和 System.Runtime.Intrinsics 命名空间进行了多项增强。这些改进包括在 .NET 8 中为 Vector256、Matrix3x2 和 Matrix4x4 提供更好的硬件加速。
Vector256 经过重新设计,内部利用 2 个 Vector128
Vector128.IsHardwareAccelerated == true,但
Vector256.IsHardwareAccelerated == false。.NET 8 还引入了 Vector512。
此外,还向硬件内部引入了 ConstExpected 属性,以在非常量值可能导致意外性能问题时提醒用户。
最后, IFloatingPointIeee754 接口中添加了 Lerp(TSelf, TSelf, TSelf) API,可以高效准确地对 float(Single)、double(Double) 和 Half 类型的两个值进行线性插值。
新的数据验证属性
DataAnnotations 命名空间专门用于云原生服务中的验证。现有的 DataAnnotations 验证器主要用于验证用户数据,如表单字段。然而,新的属性旨在验证非用户输入的数据,如配置选项。除了新的属性之外,RangeAttribute 和 RequiredAttribute 类型也新增了一些属性。
- RequiredAttribute.DisallowAllDefaultValues:该属性强制要求结构体与其默认值不相等。
- RangeAttribute.MinimumIsExclusive 和 RangeAttribute.MaximumIsExclusive:指定可接受范围是否包括其边界。
- DataAnnotations.LengthAttribute:使用 Length 属性指定字符串或集合的下限和上限。例如,[Length(5, 100)] 属性指定集合必须至少有 5 个元素,最多有 100 个元素。
- DataAnnotations.Base64StringAttribute:验证有效的 Base64 格式。
- DataAnnotations.AllowedValuesAttribute 和 DataAnnotations.DeniedValuesAttribute:指定接受的允许列表或不允许的拒绝列表。例如:[AllowedValues("red", "green", "blue")] 或 [DeniedValues("yellow", "purple")]。
函数指针自省支持
在 .NET 5 中引入了函数指针。当时还不支持反射。因此,对函数指针使用 typeof 或反射(例如typeof(delegate
Native AOT
在 .NET 7 中首次引入了作为本机 AOT 的发布方式,将应用程序发布为本机 AOT 的选项可以创建一个自包含的应用程序版本,无需单独的运行时,将所有内容捆绑到一个单独的文件中。在 .NET 8 中,本机 AOT 的支持现在包括 macOS 上的 x64 和 Arm64 架构。此外,Linux 上的本机 AOT 应用程序现在的大小缩小了多达 50%。下表显示了使用本机 AOT 发布的最小应用程序的大小,其中包含了整个 .NET 运行时:
Linux x64 (with -p:StripSymbols=true)
- .NET 7 3.76MB
- .NET 8 1.84 MB
Windows x64
- .NET 7 2.85 MB
- .NET 8 1.77 MB
代码生成改进
.NET 8 增加了对代码生成和即时编译(JIT)的改进:
- JIT 吞吐量的提升
- Arm64 性能的提升
- 支持基于程序分析的优化(PGO)的改进
- 支持 AVX-512 ISA 扩展
- SIMD (单指令多数据) 改进
- 云原生改进
- 循环和一般优化
.NET 8 DevOps 改进
容器镜像变化
在 .NET 8 中,容器镜像发生了一些变化。首先,容器镜像默认使用 Debian 12 作为 Linux 发行版。
其次,镜像中包含了一个非 root 用户,使得镜像具备非 root 用户的能力。要以非 root 用户身份运行,请在 Dockerfile 的末尾添加一行 USER app。
此外,默认端口也已从 80 更改为 8080,并且可使用新的环境变量 ASPNETCORE_HTTP_PORTS 轻松更改端口。
与 ASPNETCORE_URLS 所需的格式相比,ASPNETCORE_HTTP_PORTS 变量的格式更简单,并且接受一个端口列表。如果使用其中一种变量将端口更改回 80,将无法以非 root 用户身份运行。
最后,Chiseled Ubuntu 镜像现已支持 .NET 8,并可在 [Ubuntu/DotNet-deps Docker Hub] (Ubuntu/DotNet-deps Docker Hub)上获得。Chiseled 镜像经过精简,具有更小的攻击面,它们被精简为超紧凑,并且不包含软件包管理器或 Shell。Chiseled 镜像是非 root 用户,非常适合寻求应用类计算好处的开发人员。这些镜像定期发布到 .NET 每夜版本的存储库中,以便轻松访问。
在 Linux 上构建你的 .NET
在早期版本中,从源代码构建 .NET 在先前版本中需要从对应的发布提交中创建源代码压缩包。然而,在 .NET 8 中,这一步骤不再必要,因为 dotnet/dotnet 存储库允许在 Linux 上直接使用 dotnet/source-build 构建 .NET,以创建运行时、工具和 SDK。Red Hat 和 Canonical 也使用此构建来构建 .NET。对于大多数人来说,使用容器进行构建是最简单的方法,因为 dotnet-buildtools/prereqs 容器镜像具有所有必要的依赖关系。构建指南提供了更多信息。
Linux的最低支持基线
针对 .NET 8,Linux 的支持要求已进行更新,最低支持基线发生了变化:
所有架构都将以 Ubuntu 16.04 为目标进行 .NET 的构建,这对于设置 .NET 8 所需的最低 glibc 版本非常重要。早于 16.04 的 Ubuntu 版本(如14.04)将无法启动 .NET 8。 不再使用 .NET 8 支持 Red Hat Enterprise Linux 7,只支持 RHEL 8 及更高版本。