赞
踩
.NET 8 是一个长期支持(LTS) 版本。这篇文章涵盖了推动增强功能优先级排序和选择开发的主要主题和目标。.NET 8 预览版和发布候选版本将每月交付一次。像往常一样,最终版本将在 11 月的某个时候在 .NET Conf 上发布。
.NET 版本包括产品、库、运行时和工具,代表 Microsoft 内外多个团队之间的协作。这篇博文中涵盖的更广泛的主题并不包含 .NET 8 的所有关键场景和投资。它们代表了很大的领域,但只是进入 .NET 8 的所有重要工作的一部分。我们计划对 ASP.NET Core, Blazor, EF Core, WinForms, WPF 和其他平台进行广泛的投资。
去年年底,我们发布了 .NET 7,这是 .NET 团队与支持该版本的令人惊叹的社区合作的结果,该版本有10,000多名社区成员提供了超过28,000个社区贡献。.NET 7 是当今构建应用程序的首选框架。该版本将平台与对 ARM64 的原生支持和对 Linux 的增强支持统一起来。它有助于通过 .NET MAUI 等工具使您的应用程序现代化,这些工具可以从同一代码库构建跨平台的移动和桌面应用程序。它包括对 API 性能的改进,并使构建和部署分布式云原生应用程序变得更加容易。. NET 7 通过改进 C# 11 减少了必要的代码量,简化了构建应用程序的体验,并使得仅用几行代码就可以创建和配置 API。从帮助调试云 API 集成到直接从 .NET SDK 构建容器的开发隧道,对工具进行了大量改进,帮助开发人员提高工作效率。
我们将在整个版本中更新 .NET 8 中的新增功能。
您可以通过向下滚动阅读我们在预览1中发布的内容。首先,让我们展望一下 .NET 8 的愿景。
我们相信 .NET 开发人员应该能够将他们的应用程序快速迁移到云中,在不影响性能的情况下扩展他们的应用程序,并根据可操作的数据和关于您的应用程序在生产中的反馈来改进他们的应用程序。我们将投资于通过持续集成和部署更轻松地管理本地开发和测试的完整端到端体验。我们的目标是让微服务架构的实施以及容器的构建和部署变得更加容易。
云原生是一个术语,用于描述专门为在云计算环境中部署而构建的应用程序的架构和设计。云原生背后的主要思想是利用云计算平台提供的优势,例如可扩展性、弹性和自我修复,来创建高度可扩展和有弹性的应用程序。这提供了灵活性,并避免了为支持增长而对硬件和软件进行潜在的过度投资。许多开发人员将云原生与微服务、容器编排 (Kubernetes) 和“即服务”产品等概念联系在一起。
在 .NET 7 期间,我们发布了 .NET 多平台应用程序用户界面 (MAUI) SDK 和 Visual Studio 工具支持。.NET MAUI 提供了一个框架,用于使用单个 C# 代码库为运行 Android、iOS、macOS 和 Windows 的移动和桌面设备创建本机应用程序。除了支持 XAML UI 之外,您还可以使用 Blazor 构建具有 Razor UI 组件的混合应用程序,这些应用程序可以访问本机设备平台并在移动、桌面和 Web 之间共享。.NET 团队计划以这些经验为基础,专注于提高 SDK 和工具的质量、稳定性、性能和集成度。
.NET 的每个版本都包括对 API、库和框架的性能、质量、稳定性和易用性的改进,它们构成了活跃且不断发展的 .NET 生态系统。其中许多改进是由客户和社区成员确定并优先考虑的。.NET 8 将遵循相同的趋势,依靠您高度重视的反馈来帮助指导我们的愿景并推动我们的重点。
.NET 升级帮助是一个有价值的工具,可以帮助开发人员将他们的应用程序从旧版本的 .NET Framework 迁移到新版本。该工具的最新版本具有改进的功能,可以支持新场景并处理更多案例。有了这个工具,开发人员现在可以轻松地将他们的应用程序升级到 .NET 6或 .NET 7。
该工具可以自动检测并建议需要对代码进行的更改,以确保与较新版本的框架兼容。此外,它还可以处理更复杂的场景,例如升级使用第三方库的应用程序以及与更新的平台功能集成。这些改进使 .NET 升级辅助成为那些希望保持其应用程序最新并利用最新的 .NET 功能的开发人员的不可或缺的工具。该工具最近作为 Visual Studio 扩展引入。可帮助您从舒适的 Visual Studio 升级。
要以 .NET 8 为目标,您首先需要确保从 Microsoft 官方网站安装了 .NET 8 SDK。接下来,您可以创建一个新项目,并通过在项目设置中设置适当的目标框架来指定您希望以 .NET 8 为目标。
您还可以通过更改项目属性中的目标框架,将现有项目更新为面向 .NET 8。为此,请在 Visual Studio 或您喜欢的 IDE 中右键单击项目,选择“属性”,然后选择“应用程序”选项卡。从那里,您可以选择要使用的目标框架版本。这将设置适当的目标框架:
<TargetFramework>net8.0</TargetFramework>
请记住,以 .NET 8 为目标可能需要更改您的代码或依赖项,因为 API 或其他功能可能与以前版本的 .NET 有所不同。最好查看 .NET 8 的文档和发行说明,以确保您的代码和依赖项与新版本兼容。
我们的第一个预览版包含您今天就可以试用的新功能。以下是预期结果的摘要。有关详细的发行说明和重大更改,请阅读 .NET 8 中的新增功能。
第一个 NativeAOT 功能在 .NET 7 和目标控制台应用程序中发布。Ahead-of-Time (AOT)编译是 .NET 中的一项重要功能,可以对 .NET 应用程序的性能产生重大影响。感谢 Adeel 和 Filip 将 NativeAOT 功能引入 macOS 进行预览 1。.NET 团队将专注于完善 .NET 8 的一些基础知识,例如大小(请参阅dotnet/runtime#79003)。使用 NativeAOT 发布应用程序会创建一个完全独立的应用程序版本,不需要单独的运行时,因为所有内容都包含在单个文件中。从预览版1开始,这个文件变小了。事实上,Linux 版本现在最多缩小了 50%。
以下是包含整个 .NET 运行时的 Native AOT 的“Hello, World”应用程序的大小:
NativeAOT 将继续扩展并瞄准 .NET 8 中的其他应用场景,因此请继续关注此博客以获取未来更新!
如果您不熟悉 AOT,这里有一些 AOT 提供的好处:
.NET 开发人员可以使用容器镜像以轻量级、可移植的格式打包和部署他们的应用程序,这种格式可以在不同环境中运行并且可以轻松部署到云端中。关于如何将容器镜像用于 .NET 应用程序,预览版 1 做出了以下改进:
.NET 8 提供了一种更好的方法。从预览版 1 开始,我们发布的所有容器镜像都将支持非根配置。以下是用于以非根身份为 Dockerfiles 运行容器的单行示例:
USER app
此外,您现在可以使用 -u app。默认端口已从端口80更改为8080。这是启用非根方案所必需的重大更改,因为端口80是特权端口。
System.Random 和 System.Security.Cryptography.RandomNumberGenerator 都有实用方法 GetItems 以用于从输入集中随机选择项目(“用替换”),以及用于随机化跨度顺序的实用方法 Shuffle。
Shuffle 有助于减少机器学习中的训练偏差(因此第一件事并不总是训练,最后一件事总是测试):
- YourType[] trainingData = LoadTrainingData();
- Random.Shared.Shuffle(trainingData);
-
- IDataView sourceData = mlContext.Data.LoadFromEnumerable(trainingData);
-
- DataOperationsCatalog.TrainTestData split = mlContext.Data.TrainTestSplit(sourceData);
- model = chain.Fit(split.TrainSet);
-
- IDataView predictions = model.Transform(split.TestSet);
- ...
我们玩个游戏好吗?玩西蒙游戏怎么样?
- private static ReadOnlySpan<Button> s_allButtons = new[]
- {
- Button.Red,
- Button.Green,
- Button.Blue,
- Button.Yellow,
- };
-
- ...
-
- Button[] thisRound = Random.Shared.GetItems(s_allButtons, 31);
- // rest of game goes here ...
在可能的情况下,我们将 Vector256<T> 重新实现为内部 2x Vector128<T> ops:dotnet/runtime#76221。这能使一些函数部分加速,例如在 Arm64 上 Vector128.IsHardwareAccelerated == true 但 Vector256.IsHardwareAccelerated == false。
我们添加了 Vector512<T> 初始托管实现:dotnet/runtime#76642。与之前的工作项非常相似,这在内部实现为 2x Vector256<T> ops(因此间接实现为 4x Vector128 ops)。这能使一些函数部分加速,即使 Vector512.IsHardwareAccelerated == false 注意:Vector512 还没有直接加速,即使底层硬件支持它也是如此。此类功能应在未来的预览版中启用。
重写 Matrix3x2 和 Matrix4x4 以更好地利用硬件加速:dotnet/runtime#80091。这能使某些基准测试的性能提升高达 48 倍。6-10倍的改进更为常见。
注意:预览版2会对 Quaternion 和 Plane 进行改进。
Hardware Intrinsics 现在用 ConstExpected 属性进行注释:dotnet/runtime#80192。这能确保用户知道底层硬件何时需要常量,因此非常量值何时可能会意外损害性能。
将 Lerp API 添加到 IFloatingPointIeee754<TSelf> 从而添加到 float (System.Single), double (System.Double)和 System.Half和: dotnet/runtime#81186。这能有效且正确地执行两个值之间的线性插值。
我们不断改进 System.Text.Json,专注于增强源代码生成器在 NativeAOT 应用程序中与 ASP.NET Core 一起使用时的性能和可靠性。下表显示了预览版 1 附带的新功能:
现在可以配置对象反序列化行为,底层 JSON 负载包含无法映射到反序列化 POCO 类型成员的属性时都可以适用。这可以通过设置一个 JsonUnmappedMemberHandling 值来控制,也可以作为 POCO 类型本身的注释,全局上 JsonSerializerOptions 或通过自定义 JsonTypeInfo 相关类型的合同以编程方式进行控制:
- JsonSerializer.Deserialize<MyPoco>("""{"Id" : 42, "AnotherId" : -1 }""");
- // JsonException : The JSON property 'AnotherId' could not be mapped to any .NET member contained in type 'MyPoco'.
-
- [JsonUnmappedMemberHandling(JsonUnmappedMemberHandling.Disallow)]
- public class MyPoco
- {
- public int Id { get; set; }
- }
- IDerived value = new Derived { Base = 0, Derived =1 };
- JsonSerializer.Serialize(value); // {"Base":0,"Derived":1}
-
- public interface IBase
- {
- public int Base { get; set; }
- }
-
- public interface IDerived : IBase
- {
- public int Derived { get; set; }
- }
-
- public class Derived : IDerived
- {
- public int Base { get; set; }
- public int Derived { get; set; }
- }
该库现在附带了 snake_case 的命名策略和 kebab-case 属性名称转换。它们的用法与现有的 camelCase 命名策略类似:
- var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower };
- JsonSerializer.Serialize(new { PropertyName = "value" }, options); // { "property_name" : "value" }
现在可以使用以下命名策略:
- namespace System.Text.Json;
-
- public class JsonNamingPolicy
- {
- public static JsonNamingPolicy CamelCase { get; }
- public static JsonNamingPolicy KebabCaseLower { get; }
- public static JsonNamingPolicy KebabCaseUpper { get; }
- public static JsonNamingPolicy SnakeCaseLower { get; }
- public static JsonNamingPolicy SnakeCaseUpper { get; }
- }
感谢 @YohDeadfall 贡献了实施。
- public class MySerializer
- {
- private JsonSerializerOptions Options { get; }
-
- public MySerializer()
- {
- Options = new JsonSerializerOptions(JsonSerializerDefaults.Web) { Converters = { new MyCustomConverter() } };
- Options.MakeReadOnly(); // Make read-only before exposing the property.
- }
- }
多个新类型已添加到核心库中,开发人员能够利用新类型在常见场景中提高代码的性能。
新的命名空间 System.Collections.Frozen 提供 FrozenDictionary<TKey, TValue> 和FrozenSet<T> 集合。这些类型提供了一个不可变的表面区域,一旦创建,键或值就不可以进行任何更改。这反过来又使集合能够根据提供的数据更好地优化后续读取操作(例如 TryGetValue),从而使其能够在构造期有更多时间来优化所有未来的访问。这对于第一次使用时填充、且需在长期服务期间持续存在的集合特别有用,例如:
- private static readonly FrozenDictionary<string, bool> s_configurationData =
- LoadConfigurationData().ToFrozenDictionary(optimizeForReads: true);
- ...
- if (s_configurationData.TryGetValue(key, out bool setting) && setting)
- {
- Process();
- }
现有类型 ImmutableArray<T>.Builder 还获得了一种将其内容高效转换为 ImmutableArray<T> 的新方法。.NET 8 引入了DrainToImmutable(),它将当前内容作为不可变数组返回,并将构建器的集合重置为零长度数组,它会选择最有效的方法来执行此操作。基于元素的计数,我们可以使用此方法代替有条件地调用 ToImmutable()或 MoveToImmutable()。
新类型的另一个例子是 IndexOfAnyValues<T>,它能帮助开发人员预先投入一些时间以换取以后更快的执行速度。除了像 IndexOfAnyInRange 之类的新方法之外,还添加了接受 IndexOfAnyValues<T> 实例的新重载 IndexOfAny ,可以创建实例来表示要搜索的一组 T 值。此实例的创建处理派生任何必要的数据以优化后续搜索。例如,如果您经常搜索所有 ASCII 字母和数字以及一些标点符号字符,您之前可能会这样写:
- private static readonly char[] s_chars = "-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz".ToCharArray();
- ...
- int i = str.IndexOfAny(s_chars);
然而,这要求没有任何类型的向量化能够提高搜索效率,要么需要在每次调用 IndexOfAny 时花时间计算必要的状态以加速操作。现在,您可以这样写:
- private static readonly IndexOfAnyValues<char> s_chars = IndexOfAnyValues.Create("-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz");
- ...
- int i = str.AsSpan().IndexOfAny(s_chars);
预先计算所有该状态一次,以便在每次后续调用时都可以重用 IndexOfAny。
新类型 CompositeFormat 会自己反复这种模式。.NET 长期以来一直支持通过 API 进行字符串格式化,例如 string.Format 和 StringBuilder.AppendFormat,例如:
- static string GetMessage(int min, int max) =>
- string.Format(CultureInfo.InvariantCulture, "Range from {0} to {1}", min, max);
C# 6 添加了对字符串插值的支持,然后 C# 10 结合 .NET 6 显著提高了这些操作的效率,使得相同的操作可以写成:
- static string GetMessage(int min, int max) =>
- string.Create(CultureInfo.InvariantCulture, $"Range from {min} to {max}");
但是它是在编译时而不是在每次调用 string.Format 时执行可以预先计算的工作。 而这需要在编译时就能获取到格式字符串,以便可以在编译时对其进行解析……如果它直到运行时才获取到怎么办,例如,如果它是从资源文件或其他动态方式加载的?为此,.NET 8 添加了 CompositeFormat 类型。与 IndexOfAnyValues<T> 一样,它可以执行每次使用时需要执行的操作,然后将其取出执行一次。
- private static readonly CompositeFormat s_rangeMessage = CompositeFormat.Parse(LoadRangeMessageResource());
- ...
- static string GetMessage(int min, int max) =>
- string.Format(CultureInfo.InvariantCulture, s_rangeMessage, min, max);
这些新的重载还支持泛型参数,以避免装箱开销(boxing overheads)将所有内容都识别为 object。
.NET 8 预览版 1 还增加了对新的以性能为中心的哈希算法的支持,包括新的 XxHash3 和 XxHash128 类型,它们提供了快速 XXH3 和 XXH128 哈希算法的实现。
Publish(发布)和 Pack(打包)动词的目的是产生生产资产,这意味着它们应该生产Release 资产。在 .NET 8 中,他们将默认执行此操作。
此功能由 PublishRelease 和 PackRelease 布尔属性控制。默认为 true。
用 dotnet publish 演示该功能最简单:
- /app# dotnet new console
- /app# dotnet build
- app -> /app/bin/Debug/net8.0/app.dll
- /app# dotnet publish
- app -> /app/bin/Release/net8.0/app.dll
- app -> /app/bin/Release/net8.0/publish/
- /app# dotnet publish -p:PublishRelease=false
- app -> /app/bin/Debug/net8.0/app.dll
- app -> /app/bin/Debug/net8.0/publish/
请注意,PublishRelease 并且 PackRelease 也存在于从 7.0.200 SDK 开始的 .NET 7 中。它们在 .NET 7 中是可选的,并且必须设置 true 才能执行相同的行为。
请参阅重大更改相关文档:
.NET 现在可以直接从 dotnet/dotnet 存储库在 Linux 上构建。它使用 dotnet/source-build 构建 .NET 运行时、工具和 SDK。例如,这与 Red Hat 和 Canonical 用于构建 .NET 的构建相同。随着时间的推移,我们将扩展其功能以支持 macOS 和 Windows。
请参阅构建说明以在您自己的计算机上构建 VMR。对于许多人来说,在容器中构建是最简单的方法,因为我们的 dotnet-buildtools/prereqs 容器镜像包含所有必需的依赖项。
我们称这个新存储库为 Virtual Mono Repository (VMR)。它具有真正的 monorepo 的优势,这多亏了开发者们每天在众多现有存储库中(更有效地)所做的定期更新。我们相信,VMR 和更小的“工作存储库”之间的分离是 .NET 项目的未来。我们希望在 VMR 中更容易构建横切功能,但是,我们目前还没有实现。
从源代码构建 .NET 并将其构建成一个完整产品是可行的,我们认为这个新方法就是我们向其迈出的重要一步。
在 .NET 8 之前,从源构建是可行的,但需要从与发布版本相对应的 dotnet/installer commit 提交创建 “source tarball” 。您不再需要进行这一操作。存储库将有与每个发布版本对应的标签,以及持续跟踪产品状态的 main 和 release/8.0-previewN 分支。
我们正在发布安装了 .NET 8 的 Ubuntu Chiseled 镜像. 相比常规容器,这种类型的镜像适用于希望使用有设备式计算优势的开发人员。我们预计,当 .NET 8上线时,Canonical 和 Microsoft 都将支持 Ubuntu chiseled 镜像。
我们计划从 .NET 8 开始,发布以 Ubuntu Chiseled 形式的 dotnet/monitor 镜像。值得一提的是,monitor 镜像是我们发布的生产应用镜像。
Chiseled images 有多种优势:
您可以由此 aspnetapp sample 查看生产中的的 Chiseled 容器镜像。使用时,您只需要更改一行代码。
.NET 6 和 .NET 7 版本的 Chiseled 镜像现在已被发布到 nightly 存储库中。
我们正在更新支持 .NET 8 的 Linux 系统的最低基线。有三个变化需要注意。
重大变化主要就这些。我们仍将支持在 Arm32、Arm64 和 x64 架构上基础上使用 Linux。
请注意,这些更改仅适用于 Microsoft 内部版本。使用 source-build 构建的不同组织可能情况不同,通常为了一个构建生成构建版本仅适用于一个发行版版本,例如 Ubuntu 24.04。
下面演示了 Ubuntu 16.04 glibc 版本以及其用于其他发行版的模式。
- $ docker run --rm ubuntu:16.04 ldd --version
- ldd (Ubuntu GLIBC 2.23-0ubuntu11.3) 2.23
- Copyright (C) 2016 Free Software Foundation, Inc.
- This is free software; see the source for copying conditions. There is NO
- warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- Written by Roland McGrath and Ulrich Drepper.
Arm64 性能改进工作正在按计划进行 Issue#77010。
PGO 的基本改进正在按计划进行中 Issue#74873。
@SingleAccretion 在PR#76263 Long中启用了32 位解锁字段注册上的多注册变量提升。
1.允许 JIT 在某些情况下省略 GC 写障碍 PR#76135
2.typeof(..) 在大多数情况下不再需要助手调用,例如:
- Type GetMyType() => typeof(string);
-
- ; Method MyType():System.Type:this
3.4883EC28 sub rsp, 40
4.48B918083857FC7F0000 mov rcx, 0x7FFC57380818
5.E80DE8AB5F call CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE
6.90 nop
7.4883C428 add rsp, 40
8.48B8B800000364020000 mov rax, 0x264030000B8 ; ‘System.String’ C3 ret -; Total bytes of code: 25 +; Total bytes of code: 11
进 PR#77102, PR#77593, PR#77354, PR#77737, PR#78593, PR#78736 and PR#78783
总之,.NET 8 预览版1的发布是 Microsoft 多元化工程师团队与高度参与的开源社区之间良好协作的证明。.NET 8 中的新功能和改进是社区成员辛勤努力和奉献的成果,我们非常感谢大家做出的贡献。
该社区重视包容性和多样性,能成为其中的一员,我们很自豪。我们致力于建立一个人人都可以访问的技术生态系统。我们相信,只要我们一起努力,就可以取得伟大的胜利,我们对 .NET 的未来充满期待。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。