.NET 10 登场:LTS 版本助力开发者解锁性能与功能新高度(第一部分)

作者:微信公众号:【架构师老卢】
3-30 9:10
8

如果你是一名.NET 开发者,那就准备好迎接.NET 10 吧!它已正式发布,并且是一个长期支持(LTS)版本!这意味着在未来的三年里,它将持续获得更新、优化,并保持稳定,使其成为生产环境中一个可靠的选择。无论你是在开发高性能的 Web 应用、跨平台的移动解决方案,还是企业级软件,这个版本在整个.NET 生态系统中都带来了大量的改进。

在本文中,我们将深入探讨.NET 10 的所有新特性,从运行时的性能提升,到 SDK 的变化,从 ASP.NET Core 的更新,到 EF Core 的增强,以及更多方面。我们对这些内容进行了详细拆解,以便你能快速了解有哪些地方发生了变化,哪些得到了改进,以及它们会对你的代码产生怎样的影响。

那么,拿起你的键盘,启动你的集成开发环境(IDE),让我们一起来探索.NET 10 的新特性吧!🔥

具有扩展支持的长期支持版本

.NET 10 是.NET 9 的继任者,并且将作为一个长期支持(LTS)版本,这意味着它将在 3 年内获得更新和维护服务。

如果你想试用这个版本,可以在这里下载:[下载.NET 10](Download.NET 10)。

此外,微软在 GitHub 上设立了一个讨论板块,开发者们可以在那里提供反馈并报告问题:[GitHub 讨论](GitHub Discussions)。

###.NET 10 运行时的改进 .NET 10 的运行时进行了优化,新增的功能专注于提升性能以及减少代码中的抽象开销。下面,我们来探究一下这个版本中引入的三项主要增强功能。

数组接口方法的去虚拟化

.NET 10 的关键目标之一是减少常用语言特性中的抽象开销。为实现这一目标,即时编译器(JIT)现在能够对数组接口方法进行去虚拟化处理,从而实现更激进的优化。

在.NET 10 之前,使用简单的 for 循环遍历数组时,JIT 很容易通过移除边界检查并应用.NET 9 中引入的循环优化来进行优化:

static int Sum(int[] array)
{
    int sum = 0;
    for (int i = 0; i < array.Length; i++)
    {
        sum += array[i];
    }
    return sum;
}

然而,当使用 foreach 循环搭配像 IEnumerable 这样的接口时,会引入虚方法调用,这就阻止了诸如内联和边界检查消除等优化操作:

static int Sum(int[] array)
{
    int sum = 0;
    IEnumerable<int> temp = array;

    foreach (var num in temp)
    {
        sum += num;
    }
    return sum;
}

从.NET 10 开始,JIT 现在能够检测并去虚拟化这些调用,从而在遍历数组时优化 foreach 循环的性能。这仅仅是微软为实现.NET 中不同抽象实现之间性能对等的宏大计划的第一步。

值类型数组的栈分配

在.NET 9 中,当即时编译器(JIT)能够保证对象的生命周期不会超过其父方法时,引入了在栈上分配对象(栈分配)的能力。这种优化减少了垃圾回收器(GC)的负担,并实现了进一步的性能提升。

现在,在.NET 10 中,这种能力得到了扩展,允许对不包含 GC 引用的小型、固定大小的值类型数组进行栈分配。

示例

static void Sum()
{
    int[] numbers = { 1, 2, 3 };
    int sum = 0;

    for (int i = 0; i < numbers.Length; i++)
    {
        sum += numbers[i];
    }

    Console.WriteLine(sum);
}

在这种情况下,JIT 识别到 numbers 是一个包含三个整数的固定大小数组,并且其生命周期不会超过 Sum 方法,所以它不会在堆上分配该数组,而是将其放置在栈上,从而提高了内存效率。

这一变化有助于减少引用类型的抽象代价,进一步缩小了.NET 与低级语言之间的性能差距。

AVX10.2 支持

.NET 10 在基于 x64 的处理器上引入了对高级矢量扩展(AVX)10.2 的支持。AVX 是一套旨在提高矢量操作和并行处理速度的指令集。

这对开发者来说意味着什么呢? System.Runtime.Intrinsics.X86.Avx10v2 类中提供的新内在函数,在未来将能够在数学计算、图形处理、人工智能以及任何利用单指令多数据(SIMD)处理的应用程序中提升性能。

📢 重要提示:目前,没有硬件支持 AVX10.2,所以在兼容的硬件出现之前,对这些指令的 JIT 支持默认是禁用的。

###.NET 10 库的改进 .NET 10 的库在性能方面有了显著提升,并且引入了新的 API,这些 API 增强了数据处理能力、优化了内存使用情况,并提高了序列化效率。下面,我们来回顾一下其中最值得关注的更新。

支持使用除 SHA-1 之外的指纹查找证书

通过指纹查找证书是一项常见操作,但直到现在,X509Certificate2Collection.Find(X509FindType, Object, Boolean) 方法仅支持使用 SHA-1 进行搜索。

.NET 10 引入了一种新方法,允许指定要使用的哈希算法,在搜索证书时提高了安全性和灵活性。

示例

X509Certificate2Collection coll = store.Certificates.FindByThumbprint(HashAlgorithmName.SHA256, thumbprint);
Debug.Assert(coll.Count < 2, "Collection has too many matches, has SHA-2 been broken?");
return coll.SingleOrDefault();

这一增强功能使得可以使用像 SHA-256 或 SHA-3–256 这样的现代哈希算法安全地搜索证书,避免了与类似哈希长度相关的潜在风险。

支持以 ASCII/UTF-8 格式读取 PEM 编码的数据

隐私增强邮件(PEM)格式被广泛用于以文本形式存储证书和密钥。此前,PemEncoding 类仅适用于字符串(string)或 ReadOnlySpan,对于 ASCII 编码的文件需要进行手动转换。

.NET 10 引入了一种新方法,允许直接从 ASCII/UTF-8 文件中读取 PEM 编码的数据,消除了不必要的转换,提高了效率。

示例 在.NET 10 之前,在处理数据之前需要进行数据转换:

byte[] fileContents = File.ReadAllBytes(path);
char[] text = Encoding.ASCII.GetString(fileContents);
PemFields pemFields = PemEncoding.Find(text);

现在,可以直接读取文件而无需进行转换:

byte[] fileContents = File.ReadAllBytes(path);
PemFields pemFields = PemEncoding.FindUtf8(fileContents);

这在处理 PEM 文件时减少了内存消耗,并提高了性能。

为 DateOnly 新增 ISOWeek 重载方法

ISOWeek 类最初是为与 DateTime 配合使用而设计的,但随着 DateOnly 的引入,需要额外的支持。

.NET 10 包含了新的 ISOWeek 重载方法,以便能够直接与 DateOnly 配合使用,简化了基于周的计算,无需转换为 DateTime。

新方法

public static class ISOWeek
{
    public static int GetWeekOfYear(DateOnly date);
    public static int GetYear(DateOnly date);
    public static DateOnly ToDateOnly(int year, int week, DayOfWeek dayOfWeek);
}

这对于处理不包含时间组件的日期计算的应用程序而言,提高了精度和可用性。

ZipArchive 的性能和内存改进

ZipArchive 的性能在两个关键方面得到了提升: 1️⃣ 优化的更新模式:以前,编辑 ZIP 文件内的文件需要将所有条目加载到内存中,这会导致较高的资源消耗。现在,文件更新更加高效,降低了内存使用量。 2️⃣ 并行提取:由于数据是并行处理的,并且内部数据结构已得到优化以降低内存消耗,因此解压缩速度现在更快了。

这些增强功能使得 ZipArchive 在处理大型文件的压缩和解压缩任务时更加高效。

OrderedDictionary<TKey, TValue> 的新重载方法

OrderedDictionary<TKey, TValue> 类型通过为 TryAdd 和 TryGetValue 方法新增的重载方法得到了改进,这些重载方法现在会返回条目的索引,使得数据访问更加高效。

新重载方法

public class OrderedDictionary<TKey, TValue>
{
    public bool TryAdd(TKey key, TValue value, out int index);
    public bool TryGetValue(TKey key, out TValue value, out int index);
}

这使得开发人员可以利用返回的索引进行更快的访问和修改操作。

示例用法

if (!orderedDictionary.TryAdd(key, 1, out int index))
{
    int value = orderedDictionary.GetAt(index).Value;
    orderedDictionary.SetAt(index, value + 1);
}

这一改进已经应用到了 JsonObject 中,在属性更新方面实现了 10% 到 20% 的性能提升。

在 JsonSourceGenerationOptions 中支持 ReferenceHandler

在使用源生成器进行 JSON 序列化时,以前默认情况下循环引用会导致错误。在.NET 10 中,现在可以使用 ReferenceHandler 来配置序列化行为。

示例

[JsonSourceGenerationOptions(ReferenceHandler = JsonKnownReferenceHandler.Preserve)]
[JsonSerializable(typeof(SelfReference))]
internal partial class ContextWithPreserveReference : JsonSerializerContext
{
}

internal class SelfReference
{
    public SelfReference Me { get; set; } = null!;
}

这使得在 JSON 序列化中能够更好地控制引用处理,防止出现错误,并提高了与复杂数据结构的兼容性。

###.NET 10 SDK 的变化 .NET 10 SDK 在依赖项管理方面引入了重大改进,优化了构建过程并减少了磁盘空间的占用。其中一项关键更新是移除了不必要的由框架提供的包引用,这有助于提升项目的性能和安全性。

修剪框架提供的包引用

从.NET 10 开始,NuGet 审计功能现在可以自动从你的项目中移除未使用的由框架提供的包引用。

这项改进对开发有何影响呢? ✅ 更快的构建时间:在构建过程中,需要还原和分析的包更少了。 ✅ 减少磁盘空间占用:移除了不必要的依赖项,优化了项目大小。 ✅ 在安全审计中减少误报:像 NuGet 审计和其他依赖项扫描工具将产生更少的错误警报。

当启用此功能时,应用程序生成的.deps.json 文件将包含更少的包引用,因为那些已经由.NET 运行时提供的包会被自动排除在外。

这项功能默认启用吗? 是的,对于以下目标框架(TFM),此功能默认启用:

  • .NET 8.0 和.NET 10.0
  • .NET Standard 2.0 及更高版本

如果需要,如何禁用它呢? 如果你的项目需要保留所有由框架提供的包引用,你可以通过在你的.csproj 或 Directory.Build.props 文件中添加以下属性来禁用此功能:

<PropertyGroup>
    <RestoreEnablePackagePruning>false</RestoreEnablePackagePruning>
</PropertyGroup>

这可确保所有包引用都保留在.deps.json 文件中。

###.NET Aspire 9.1 的新特性 .NET Aspire 9.1 引入了多项改进,重点关注用户体验、仪表板自定义以及本地开发优化。此版本同时支持.NET 8(长期支持版本)和.NET 9(短期支持版本),使开发人员能够在任一平台上利用最新特性。

此版本的主要目标是“打磨、打磨、再打磨”,解决社区反馈的问题,并在整个平台上提升整体的使用体验。

升级到.NET Aspire 9.1

升级到新版本非常简单: 1️⃣ 在你的应用主机项目文件(MyApp.AppHost.csproj)中更新 SDK:

<Project Sdk="Microsoft.NET.Sdk">
    <Sdk Name="Aspire.AppHost.Sdk" Version="9.1.0" />
</Project>

2️⃣ 使用 Visual Studio 的 NuGet 包管理器或在 VS Code 中通过命令行检查 NuGet 包更新。 3️⃣ 通过运行以下命令更新.NET Aspire 模板:

dotnet new update

如果你的应用主机项目文件没有引用 Aspire.AppHost.Sdk,你可能仍在使用.NET Aspire 8。在这种情况下,请参阅.NET Aspire 9 的升级文档。

改进的入门体验

.NET Aspire 9.1 通过引入以下内容使入门变得更加容易:

  • GitHub Codespaces 模板,该模板预安装了所有必要的依赖项、模板以及 ASP.NET Core 开发人员证书。
  • 支持在 Visual Studio Code 中使用开发容器,提高了开发环境之间的可移植性和一致性。

有关更多详细信息,请参阅: 🔗 [使用.NET Aspire 与 GitHub Codespaces](Using.NET Aspire with GitHub Codespaces) 🔗 [在.NET Aspire 中设置开发容器](Setting up Dev Containers in.NET Aspire)

仪表板的用户体验和自定义

每个.NET Aspire 版本都对仪表板进行了增强,9.1 版本也不例外。主要改进包括:

  1. 资源关系:仪表板现在支持资源的父子关系。
    • 示例:当创建一个包含多个数据库的 Postgres 实例时,它们现在会在“资源”页面中嵌套在同一个实例下。
  2. 本地化覆盖:用户现在可以手动设置仪表板的语言,独立于浏览器的语言设置。
  3. 清除日志和遥测数据:在“控制台日志”、“结构化日志”、“跟踪”和“指标”页面中添加了新按钮,可用于清除数据。在“设置”中添加了一个“全部删除”按钮,可一次性重置所有内容。
  4. 资源筛选:用户现在可以按类型、状态和健康状况筛选资源,以获得更好的可见性。
  5. 更多资源详细信息:当选择一个资源时,详细信息窗格现在会显示:
    • 引用和反向引用
    • 带有挂载类型的卷
    • 其他元数据,以便更好地了解情况

有关更多详细信息,请查看 [.NET Aspire 仪表板资源页面](.NET Aspire Dashboard Resources page)。

对自定义本地域的跨域资源共享(CORS)支持

开发人员现在可以设置 DOTNET_DASHBOARD_CORS_ALLOWED_ORIGINS 环境变量,以允许来自自定义浏览器应用程序(例如,在自定义 localhost 域上运行的应用程序)的遥测数据。

有关更多详细信息,请访问: 🔗 [配置.NET Aspire 仪表板](Configuring the.NET Aspire dashboard)

控制台日志增强功能

新选项

  • 下载日志以便使用外部诊断工具进行分析。
  • 可切换时间戳的显示开/关,以获得更清晰的视图。

有关更多详细信息,请查看: 🔗 [.NET Aspire 仪表板中的控制台日志](Console Logs in.NET Aspire Dashboard)

本地开发增强功能

.NET Aspire 9.1 引入了多项改进,以简化本地开发流程:

  1. 按需启动资源:资源无需随应用程序自动启动。开发人员现在可以使用 WithExplicitStart 标记资源,从而可以通过仪表板手动启动它们。
    • 有关更多详细信息:[配置显式资源启动](Configuring Explicit Resource Start)
  2. 改进的 Docker 集成:.PublishAsDockerfile() 现在适用于所有项目和可执行资源,实现了对容器的更好自定义。
  3. 清理 Docker 网络:修复了在停止 Aspire 应用程序后 Docker 网络未被删除的问题。现在,网络会被正确清理,确保更高效的开发环境。

错误修复和稳定性改进

.NET Aspire 9.1 解决了多个已报告的问题,包括:

  1. 修复“地址已在使用”错误:改进了地址管理,以防止在重启期间发生冲突。
  2. 更好的集成处理:对 Azure 服务总线、事件中心和 Cosmos DB 进行了各种更新,提高了兼容性和易用性。
  3. 服务总线和事件中心增强功能:开发人员现在可以直接在应用主机代码中定义服务总线队列、主题和事件中心。
    • 🔗 更多信息:
    • [Azure 服务总线集成](Azure Service Bus Integration)
    • [Azure 事件中心集成](Azure Event Hubs Integration)
相关留言评论
昵称:
邮箱:
阅读排行