.NET 7 今天可用

为应用带来 更高的性能 和新功能, .NET 7 为C# 11 / F# 7 、 .NET MAUI 、 ASP.NET Core/Blazor 、Web API、 WinForms 、 WPF 。 使用 .NET 7,您还可以轻松地将 .NET 7 项目容器化,在 GitHub 操作中设置 CI/CD 工作流,并实现云原生可观察性。感谢 开源 .NET 社区 为塑造此 .NET 7 版本做出的众多贡献。 在整个 .NET 7 版本中,超过 8900 名贡献者做出了 28k 贡献 !

.NET 仍然是 最快 最受喜爱 和最受信任的平台之一,它拥有一个广泛的 .NET 包生态系统 ,其中包括超过 330,000 个包。

下载和升级

您可以 的免费 .NET 7 版本。 立即下载适用于 Windows、macOS 和 Linux

, .NET 7 提供了直接的升级,如果 如果您使用的是.NET Core 版本 ,则提供了一些令人信服的迁移理由 您当前维护的是.NET Framework 版本

Visual Studio 2022 17.4 今天也可用。 在 Visual Studio 2022 中开发 .NET 7 可为开发人员提供一流的生产力工具。 要了解 Visual Studio 2022 中的新增功能,请查看 Visual Studio 2022 博客

.NET 7 中的新功能

.NET 7 版本与其他几个产品、库和平台一起发布,包括:

在这篇博文中,我们将重点介绍 .NET 团队专注于交付的主要主题:

  • 统一 一个 BCL
  • 新的 TFM
  • 对 ARM64 的本机支持
  • Linux 上增强的 .NET 支持
  • 现代的 持续的性能改进
  • 开发人员生产力增强,例如容器优先的工作流程
  • 从相同的代码库构建跨平台的移动和桌面应用程序
  • .NET 适用于云原生应用 易于构建和部署分布式云原生应用
  • 简单的 使用 C# 11 简化和编写更少的代码
  • 针对云原生应用程序的 HTTP/3 和最小 API 改进
  • 表现 多项性能改进

下面,我们将更详细地介绍这些主题,并分享有关这项工作为何如此重要的更多背景信息。

场景

.NET 7 用途广泛,您可以在任何平台上构建任何应用程序。

让我们重点介绍从今天开始可以使用 .NET 实现的一些场景:

统一

一个基类库 (BCL)

.NET 7 版本是我们 .NET 统一之旅中的第三个主要版本(自 2016 年 .NET 5 以来)。

使用 .NET 7,您只需学习一次,就可以通过一个 SDK、一个运行时、一组基础库重复使用您的技能来构建多种类型的应用程序(云、Web、桌面、移动、游戏、IoT 和 AI)。

面向 .NET 7

当您以应用程序或库中的框架为目标时,您正在指定要提供的 API 集。 要以 .NET 7 为目标,只需更改项目中的目标框架即可。

<TargetFramework>net7.0</TargetFramework>

以应用程序为目标 net7.0目标框架名字对象 (TFM) 将适用于所有受支持的操作系统和 CPU 架构。 它们使您可以访问 .NET 7 中的所有 API 以及一堆特定于操作系统的 API,例如:

  • net7.0-安卓
  • net7.0-ios
  • net7.0-maccatalyst
  • net7.0-macos
  • net7.0-tvos
  • net7.0-windows

通过公开的 API net7.0TFM 设计用于在任何地方工作。 如果您怀疑 .NET 7 是否支持 API,您可以随时查看 https://apisof.net/。 这是新添加的界面的示例 IJsonTypeInfoResolver您可以看到它现在已内置到 .NET 7 中:

ARM64

随着行业向 ARM 发展,.NET 也是如此。 ARM CPU 的最大优势之一是电源效率。 这以最低的功耗带来最高的性能。 换句话说,您可以事半功倍。 在 .NET 5 中, 我们描述了我们针对 ARM64 所做的性能计划 。 现在,在两个版本之后,我们想与您分享我们已经走了多远。 我们的持续目标是将 x64 的性能与 ARM64 相匹配,以帮助我们的客户将他们的 .NET 应用程序迁移到 ARM。

运行时改进

我们在调查 x64 和 ARM64 时遇到的一个挑战是发现 L3 缓存大小没有从 ARM64 机器正确读取。 如果无法从操作系统或机器的 BIOS 中获取 L3 缓存大小,我们更改了启发式方法以返回近似大小。 现在我们可以更好地估算每个 L3 缓存大小的核心数。

核心数 L3 缓存大小 1~4 4MB 5~16 8MB 17~64 16MB 65+ 32MB

接下来是我们对 LSE 原子的理解。 如果您不熟悉,它提供了原子 API 来获得对关键区域的独占访问权限。 在 CISC 架构 x86-x64 机器中,内存上的读-修改-写 (RMW) 操作可以通过添加锁定前缀的单个指令执行。

但是在RISC架构的机器上,RMW操作是不允许的,所有的操作都是通过寄存器来完成的。 因此,对于并发场景,它们有一对指令。 “Load Acquire” (ldaxr) 获得对内存区域的独占访问权,这样其他内核就无法访问它,而“Store Release” (stlxr) 则释放对其他内核的访问权限。 在这些对之间,执行关键操作。 如果在您使用 ldaxr 加载内容后,由于其他 CPU 对内存进行操作而导致 stlxr 操作失败,则有一个代码可以重试(cbnz 跳回重试)该操作。

ARM 在 v8.1 中引入了 LSE 原子指令。 有了这些指令,这些操作可以比传统版本用更少的代码和更快的速度完成。 当我们为 Linux 启用此功能并随后将其扩展到 Windows 时,我们看到了大约 45% 的性能提升。

图书馆改进

为了优化使用内在函数的库,我们添加了新的跨平台助手。 这些包括帮助 Vector64, Vector128, 和 Vector256. 跨平台助手允许通过用与硬件无关的内在函数替换特定于硬件的内在函数来统一矢量化算法。 这将使任何平台上的用户受益,但我们预计 ARM64 将受益最大,因为没有 ARM64 专业知识的开发人员仍然能够使用帮助程序来利用 Arm64 硬件内在函数。

重写 API,例如 EncodeToUtf8和 DecodeFromUtf8从 SSE3 实现到基于向量的实现可以提供高达 60% 的改进。

类似地转换其他 API,例如 NarrowUtf16ToAscii()和 GetIndexOfFirstNonAsciiChar()可以证明性能提升高达 35%。

性能影响

通过我们在 .NET 7 中的工作,许多 MicroBenchmark 提高了 10-60%。 当我们开始 .NET 7 时,ARM64 的每秒请求数 (RPS) 较低,但慢慢克服了 x64 的奇偶校验。

同样对于延迟(以毫秒为单位),我们将桥接 x64 的奇偶校验。

有关更多详细信息,请查看 .NET 7 中的 ARM64 性能改进

Linux 上增强的 .NET 支持

.NET 6 包含在 Ubuntu 22.04 (Jammy) 中,可以使用 apt install dotnet6命令。 此外,还有一个优化的、预构建的、开箱即用的超小型容器镜像。

dotnetapp % docker run --rm dotnetapp-chiseled
         42
         42              ,d                             ,d
         42              42                             42
 ,adPPYb,42  ,adPPYba, MM42MMM 8b,dPPYba,   ,adPPYba, MM42MMM
a8"    `Y42 a8"     "8a  42    42P'   `"8a a8P_____42   42
8b       42 8b       d8  42    42       42 8PP"""""""   42
"8a,   ,d42 "8a,   ,a8"  42,   42       42 "8b,   ,aa   42,
 `"8bbdP"Y8  `"YbbdP"'   "Y428 42       42  `"Ybbd8"'   "Y428
.NET 7.0.0-preview.7.22375.6
Linux 5.10.104-linuxkit #1 SMP PREEMPT Thu Mar 17 17:05:54 UTC 2022
OSArchitecture: Arm64
ProcessorCount: 4
TotalAvailableMemoryBytes: 3.83 GiB

有关我们与 Canonical 和 ARM 合作的更多信息,请阅读 .NET 6 now in Ubuntu 22.04

64 位 IBM Power 支持

除了 x64 架构(64 位 Intel/AMD)、ARM64(64 位 ARM)和 s390x(64 位 IBM Z)之外,.NET 现在也可用于针对 RHEL 的 ppc64le(64 位 IBM Power)架构8.7 和 RHEL 9.1。

由于现在可以在 Power 上本地运行,超过 25,000 名 IBM Power 客户可以在 Windows x86 上整合现有的 .NET 应用程序,以便在与其 IBM i 和 AIX 业务应用程序和数据库相同的 Power 平台上运行。 这样做可以显着提高可持续性,将碳足迹减少多达 5 倍,并结合 RHEL 和 OpenShift 容量的本地即用即付扩展,同时提供行业领先的端到端企业事务和数据安全性。

现代的

.NET 7 专为现代云原生应用、移动客户端、边缘服务和桌面技术而构建。 使用单个代码库创建移动体验,而不会影响使用 .NET MAUI 的本机性能。 使用熟悉的技术(如 C# 和 Razor 模板)构建在浏览器中运行并作为渐进式 Web 应用程序 (PWA) 离线运行的响应式单页应用程序 (SPA)。 这些更快的现代体验不仅适用于新应用程序。 .NET 升级助手将提供有关兼容性的反馈,并在某些情况下将您的应用程序完全迁移到 .NET 6 和 .NET 7。

.NET 已修复

NET MAUI 现在是 .NET 7 的一部分,具有大量改进和新功能。 ,您可以了解 .NET MAUI 以及它使您能够为所有移动设备构建应用程序的方式 通过阅读最新的.NET MAUI 博客公告

布雷泽

Blazor 不断发展,.NET 7 包括许多重大改进。 Blazor 现在支持处理位置更改事件,改进了 WebAssembly 调试体验,以及对使用 OpenID Connect 进行身份验证的开箱即用支持。 要了解更多信息,请阅读最新的 Blazor 团队博客文章

升级助手

.NET 升级助手提供分步指导、见解和自动化,将您的旧应用程序迁移到 .NET 6 和 .NET 7。在某些情况下,它可以为您执行迁移! 在对旧代码库进行现代化改造时,它有助于减少时间和复杂性。 例如,了解如何在 CoreWCF 的帮助下将您的 WCF 应用程序带到 .NET Core。 使用 .NET 7,改进的体验包括:

  • ASP.NET 到 ASP.NET Core
  • System.Web 适配器(预览版)
  • 增量迁移(预览)
  • 为 WinForms、WPF 和控制台/类库添加了更多分析器和代码修复程序
  • 分析二进制文件的能力
  • UWP 到 Windows 应用 SDK 和 WinUI 支持

准备好将您的应用程序迁移到迄今为止最新且性能最快的 .NET 上了吗? 立即下载升级助手

.NET 6 迁移亮点

在去年宣布 .NET 6 之后,有许多成功的旅程到最新版本的 .NET。 这些故事突出了显着改善 CPU 使用率、增加每秒请求数 (RPS)、更好地使用线程池、减少二进制文件大小、更快启动时间、简化依赖项管理、降低未来技术债务、降低基础设施成本的好处,最重要的是:工程满意度和生产力。

有关于迁移到最新版本 .NET 的故事吗? 请在下面的评论中告诉我们。

.NET 适用于云原生应用

.NET 7 使开箱即用地构建云原生应用程序变得前所未有的容易。 使用 Visual Studio 的连接服务安全地连接到数据服务并安全地加密用户机密文件或 Azure Key Vault 中的连接字符串。 将您的应用程序直接构建到容器映像中。 使用 Entity Framework 7 编写强类型语言集成查询 (LINQ) 查询,这些查询使用 SQL Server 的 JSON 支持从存储在关系数据库中的 JSON 文档中快速提取内容。 只需几行代码,即可通过经过身份验证的端点交付安全的 JSON 文档,并提供最少的 API 体验。 使用 Open Telemetry 收集有关您正在运行的应用程序的见解。

零日 Azure 支持

.NET 7 不仅非常适合构建云原生应用程序; Azure 的 PaaS 服务,如适用于 Windows 和 Linux 的应用服务、静态 Web 应用、Azure Functions 和 Azure 容器应用 ,今天已为 .NET 7 做好准备 ,连续第三个版本发布,如 .NET 5.0 和 6.0。 在发布的第一周,您可能会遇到 .NET 7 应用程序的启动时间稍长一些,因为将为使用 .NET 7 创建新应用服务的客户及时安装 .NET 7 SDK。此外,如果您运行的是 .NET 7 预览版,只需重新启动应用服务即可将您更新到 GA 位。

内置容器支持

容器的普及和实际使用正在上升,对于许多公司来说,它们代表了部署到云的首选方式。 但是,使用容器会为团队的积压工作增加新的工作,包括构建和发布镜像、检查安全性和合规性以及优化镜像的性能。 我们相信有机会使用 .NET 容器创建更好、更简化的体验。

您现在可以创建应用程序的容器化版本,只需 dotnet publish. 我们构建此解决方案的目标是与现有构建逻辑无缝集成,利用我们自己丰富的 C# 工具和运行时性能,并直接内置到 .NET SDK 的盒子中以进行定期更新。

容器图像现在是 .NET SDK 支持的输出类型:

# create a new project and move to its directory
dotnet new mvc -n my-awesome-container-app
cd my-awesome-container-app
# add a reference to a (temporary) package that creates the container
dotnet add package Microsoft.NET.Build.Containers
# publish your project for linux-x64
dotnet publish --os linux --arch x64 -p:PublishProfile=DefaultContainer

要了解有关内置容器支持的更多信息,请参阅 宣布对 .NET SDK 的内置容器支持

微软奥尔良

Microsoft Orleans 7.0 将使用“普通旧 CLR 对象”(POCO) Grains 提供更简单的编程模型,提供比 3.x 高 150% 的性能,并引入新的序列化和不变性改进。 ASP.NET Core 开发人员可以使用 Orleans 简单地添加分布式状态,并确信他们的应用程序将在不增加复杂性的情况下水平扩展。 我们将继续投资以使 Orleans 功能更接近 ASP.NET 堆栈,以确保您的 Web 和 API 应用程序为云规模、分布式托管方案甚至多云部署做好准备。 Orleans 支持大多数流行的存储机制和数据库,并且能够在 ASP.NET Core 可以运行的任何地方运行,Orleans 是让您的 .NET 应用程序具有云原生分布式功能的绝佳选择,而无需学习新的框架或工具集。 了解更多关于奥尔良 7 的信息。

可观察性

可观察性的目标是帮助您更好地了解应用程序在扩展和技术复杂性增加时的状态。 .NET 接受 了 OpenTelemetry ,同时还在 .NET 7 中进行了以下改进。

介绍 Activity.Current 更改事件

分布式跟踪的典型实现使用 AsyncLocal<T>跟踪托管线程的“跨度上下文”。 通过使用 AsyncLocal<T>构造函数接受 valueChangedHandler范围。 然而,随着 Activity成为 OpenTelemetry 使用的表示 span 的标准,因此无法设置值更改处理程序,因为通过以下方式跟踪上下文 Activity.Current. 可以使用新的更改事件来接收所需的通知。

Activity.CurrentChanged += CurrentChanged;<
>>
void CurrentChanged(object? sender, ActivityChangedEventArgs e)
{
  Console.WriteLine($"Activity.Current value changed from Activity:  {e.Previous.OperationName} to Activity: {e.Current.OperationName}");
}

公开高性能活动属性枚举器方法

以下新公开的方法可以在性能关键场景中用于枚举 Activity的 Tags, Links, 和 Events没有任何额外分配和高性能项目访问的属性:

Activity a = new Activity("Root");<
>>
a.SetTag("key1", "value1");
a.SetTag("key2", "value2");
foreach (ref readonly KeyValuePair<string, object?> tag in a.EnumerateTagObjects())
{
  Console.WriteLine($"{tag.Key}, {tag.Value}");
}

公开高性能 ActivityEvent 和 ActivityLink 标签枚举器方法

与上述类似, ActivityEvent和 ActivityLink标记对象也被公开以减少对高性能项目访问的任何额外分配:

var tags = new List<KeyValuePair<string, object?>>()
{
  new KeyValuePair<string, object?>("tag1", "value1"),
  new KeyValuePair<string, object?>("tag2", "value2"),
};
ActivityLink link = new ActivityLink(default, new ActivityTagsCollection(tags));
foreach (ref readonly KeyValuePair<string, object?> tag in link.EnumerateTagObjects())
{
// Consume the link tags without any extra allocations or value copying.
}
ActivityEvent e = new ActivityEvent("SomeEvent", tags: new ActivityTagsCollection(tags));
foreach (ref readonly KeyValuePair<string, object?> tag in e.EnumerateTagObjects())
{
// Consume the event's tags without any extra allocations or value copying.
}

简单的

C# 11 & F# 7

C# 和 F# 语言的最新添加是 C# 11 F# 7 。 C# 11 使通用数学等新功能成为可能,同时通过对象初始化改进、原始字符串文字等简化代码。

通用数学

.NET 7 为基类库引入了新的数学相关通用接口。 这些接口的可用性意味着您可以将泛型类型或方法的类型参数约束为“类似数字”。 此外,C# 11 及更高版本允许您定义静态虚拟接口成员。 因为运算符必须声明为静态,所以这个新的 C# 功能允许在新接口中为类似数字的类型声明运算符。

总之,这些创新让您可以通用地执行数学运算,也就是说,无需知道您正在使用的确切类型。 例如,如果您想编写一个添加两个数字的方法,之前您必须为每种类型添加方法的重载(例如, static int Add(int first, int second)和 static float Add(float first, float second). 现在您可以编写一个单一的泛型方法,其中类型参数被限制为类似数字的类型。

static T Add<T>(T left, T right) where T : INumber<T>
{
    return left + right;
}

在此方法中,类型参数 T 被约束为实现新的类型 INumber<TSelf> 界面。 INumber<TSelf> 实现 IAdditionOperators<TSelf,TOther,TResult> 接口,其中包含 + operator . 这允许该方法通常将两个数字相加。 该方法可以与 .NET 的任何内置数字类型一起使用,因为它们都已更新为实现 INumber<TSelf>在 .NET 7 中。

库作者将从通用数学接口中受益最多,因为他们可以通过删除“冗余”重载来简化他们的代码库。 其他开发人员将间接受益,因为他们使用的 API 可能会开始支持更多类型。

查看 有关通用数学的文档, 了解有关每个接口公开的核心 API 的更多信息。

原始字符串文字

现在有一种新的字符串文字格式。 原始字符串文字可以包含任意文本,包括空格、换行符、嵌入引号和其他特殊字符,而无需转义序列。 原始字符串文字至少以三个双引号 (""") 字符开头,并以相同数量的双引号字符结尾。

string longMessage = """
    This is a long message.
    It has several lines.
        Some are indented
                more than others.
    Some should start at the first column.
    Some have "quoted text" in them.
    """;

.NET 库

许多 .NET 的第一方库在 .NET 7 版本中得到了显着改进。 您将看到对 Microsoft.Extensions.* 包的可为空注释的支持、System.Text.Json 的合同自定义和类型层次结构,以及帮助您以磁带存档 (TAR) 格式编写数据的新 Tar API 等等。

Microsoft.Extensions 的可空注释

所有 Microsoft.Extensions.* 库现在都包含 C# 8 选择加入功能,该功能允许编译器跟踪引用类型可空性以捕获潜在的空取消引用。 这可以帮助您将代码导致运行时抛出 System.NullReferenceException.

System.Composition.Hosting

添加了一个新 API 以 允许将单个对象实例 添加到 System.Composition.Hosting容器提供与遗留接口类似的功能 System.ComponentModel.Composition.Hosting通过 API ComposeExportedValue(CompositionContainer, T).

namespace System.Composition.Hosting
{
  public class ContainerConfiguration
  {
      public ContainerConfiguration WithExport<TExport>(TExport exportedInstance);
      public ContainerConfiguration WithExport<TExport>(TExport exportedInstance, string contractName = null, IDictionary<string, object> metadata = null);
      public ContainerConfiguration WithExport(Type contractType, object exportedInstance);
      public ContainerConfiguration WithExport(Type contractType, object exportedInstance, string contractName = null, IDictionary<string, object> metadata = null);
  }
}

将微秒和纳秒添加到 TimeStamp、DateTime、DateTimeOffset 和 TimeOnly

在 .NET 7 之前,各种日期和时间结构中可用的最低时间增量是 Ticks 属性中可用的“滴答”。 作为参考,一个滴答声是 100ns。 传统上,开发人员必须对“滴答”值执行计算以确定微秒和纳秒值。 在 .NET 7 中,我们在日期和时间实现中引入了微秒和纳秒:

namespace System
{
  public struct DateTime
  {
    public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond);
    public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond, System.DateTimeKind kind);
    public DateTime(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond, System.Globalization.Calendar calendar);
    public int Microsecond { get; }
    public int Nanosecond { get; }
    public DateTime AddMicroseconds(double value);
  }
  public struct DateTimeOffset
  {
    public DateTimeOffset(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond, System.TimeSpan offset);
    public DateTimeOffset(int year, int month, int day, int hour, int minute, int second, int millisecond, int microsecond, System.TimeSpan offset, System.Globalization.Calendar calendar);
    public int Microsecond { get; }
    public int Nanosecond { get; }
    public DateTimeOffset AddMicroseconds(double microseconds);
  }
  public struct TimeSpan
  {
    public const long TicksPerMicrosecond = 10L;
    public const long NanosecondsPerTick = 100L;
    public TimeSpan(int days, int hours, int minutes, int seconds, int milliseconds, int microseconds);
    public int Microseconds { get; }
    public int Nanoseconds { get; }
    public double TotalMicroseconds { get; }
    public double TotalNanoseconds { get; }
    public static TimeSpan FromMicroseconds(double microseconds);
  }
  public struct TimeOnly
  {
    public TimeOnly(int hour, int minute, int second, int millisecond, int microsecond);
    public int Microsecond { get; }
    public int Nanosecond { get; }
  }
}

Microsoft.Extensions.Caching

我们添加了指标支持 IMemoryCache, 这是一个新的 API MemoryCacheStatistics保存缓存命中、未命中和估计大小 IMemoryCache. 你可以得到一个实例 MemoryCacheStatistics通过调用 GetCurrentStatistics()当国旗 TrackStatistics已启用。

这 GetCurrentStatistics()API 允许应用程序开发人员使用事件计数器或指标 API 来跟踪一个或多个内存缓存的统计信息。

// when using `services.AddMemoryCache(options => options.TrackStatistics = true);` to instantiate
[EventSource(Name = "Microsoft-Extensions-Caching-Memory")]
internal sealed class CachingEventSource : EventSource
{
  public CachingEventSource(IMemoryCache memoryCache)
  {
    _memoryCache = memoryCache;
  }
  protected override void OnEventCommand(EventCommandEventArgs command)
  {
    if (command.Command == EventCommand.Enable)
    {
      if (_cacheHitsCounter == null)
      {
          _cacheHitsCounter = new PollingCounter("cache-hits", this, () => _memoryCache.GetCurrentStatistics().CacheHits)
          {
            DisplayName = "Cache hits",
          };
      }
    }
  }
}

然后,您可以使用以下命令查看统计信息 dotnet-counters工具:

Press p to pause, r to resume, q to quit.
    Status: Running
[System.Runtime]
    CPU Usage (%)                                      0
    Working Set (MB)                                  28
[Microsoft-Extensions-Caching-MemoryCache]
    cache-hits                                       269

System.Formats.Tar API

我们添加了一个新的 System.Formats.Tar包含允许读取、写入、归档和提取 Tar 档案的跨平台 API 的程序集。 SDK 甚至使用这些 API 来创建容器作为发布目标。

// Generates a tar archive where all the entry names are prefixed by the root directory 'SourceDirectory'
TarFile.CreateFromDirectory(sourceDirectoryName: "/home/dotnet/SourceDirectory/", destinationFileName: "/home/dotnet/destination.tar", includeBaseDirectory: true);
// Extracts the contents of a tar archive into the specified directory, but avoids overwriting anything found inside
TarFile.ExtractToDirectory(sourceFileName: "/home/dotnet/destination.tar", destinationDirectoryName: "/home/dotnet/DestinationDirectory/", overwriteFiles: false);

类型转换器

现在有用于新添加的原始类型的公开类型转换器 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));
// produce DateOnly value of DateOnly(1940, 10, 9)
DateOnly? date = dateOnlyConverter.ConvertFromString("1940-10-09") as DateOnly?;
TypeConverter timeOnlyConverter = TypeDescriptor.GetConverter(typeof(TimeOnly));
// produce TimeOnly value of TimeOnly(20, 30, 50)
TimeOnly? time = timeOnlyConverter.ConvertFromString("20:30:50") as TimeOnly?;
TypeConverter halfConverter = TypeDescriptor.GetConverter(typeof(Half));
// produce Half value of -1.2
Half? half = halfConverter.ConvertFromString(((Half)(-1.2)).ToString()) as Half?;
TypeConverter Int128Converter = TypeDescriptor.GetConverter(typeof(Int128));
// produce Int128 value of Int128.MaxValue which equal 170141183460469231731687303715884105727
Int128? int128 = Int128Converter.ConvertFromString("170141183460469231731687303715884105727") as Int128?;
TypeConverter UInt128Converter = TypeDescriptor.GetConverter(typeof(UInt128));
// produce UInt128 value of UInt128.MaxValue Which equal 340282366920938463463374607431768211455
UInt128? uint128 = UInt128Converter.ConvertFromString("340282366920938463463374607431768211455") as UInt128?;

System.Text.Json 合约定制

System.Text.Json 通过为该类型构造 JSON 契约来确定给定 .NET 类型如何被序列化和反序列化。 契约派生自类型的形状——例如其可用的构造函数、属性和字段,以及它是否实现 IEnumerable或者 IDictionary— 在运行时使用反射或在编译时使用源生成器。 对派生合约进行有限的调整。 使用System.Text.Json 属性注释 在以前的版本中,假设用户能够修改类型声明,他们可以

给定类型的合约元数据 T表示使用 JsonTypeInfo<T>,在以前的版本中,它用作源生成器 API 中专用的不透明令牌。 从 .NET 7 开始, JsonTypeInfo合同元数据已公开并可供用户修改。 合同定制允许用户使用 IJsonTypeInfoResolver界面:

public interface IJsonTypeInfoResolver
{
  JsonTypeInfo? GetTypeInfo(Type type, JsonSerializerOptions options);
}

合约解析器返回一个已配置的 JsonTypeInfo 给定的实例 Type 和 JsonSerializerOptions 组合。 它可以返回 null如果解析器不支持指定输入类型的元数据。

默认的基于反射的序列化程序执行的合约解析现在通过 DefaultJsonTypeInfoResolver类,它实现 IJsonTypeInfoResolver. 此类允许用户通过自定义修改扩展默认的基于反射的解析,或将其与其他解析器(例如源生成的解析器)结合使用。

从 .NET 7 开始 JsonSerializerContext源生成中使用的类也实现 IJsonTypeInfoResolver. 要了解有关源生成器的更多信息,请参阅 如何在 System.Text.Json 中使用源生成。

一个 JsonSerializerOptions可以使用新的自定义解析器配置实例 TypeInfoResolver财产:

// configure to use reflection contracts
var reflectionOptions = new JsonSerializerOptions
{
    TypeInfoResolver = new DefaultJsonTypeInfoResolver()
};
// configure to use source generated contracts
var sourceGenOptions = new JsonSerializerOptions
{
    TypeInfoResolver = EntryContext.Default
};
[JsonSerializable(typeof(MyPoco))]
public partial class EntryContext : JsonSerializerContext { }

查看 [What's new in System.Text.Json in .NET 7] 博客文章,了解有关合同自定义的更多详细信息。

System.Text.Json 类型层次结构

System.Text.Json 现在支持用户定义类型层次结构的多态序列化和反序列化。 这可以通过用新的修饰类型层次结构的基类来实现 JsonDerivedTypeAttribute

[JsonDerivedType(typeof(Derived))]
public class Base
{
    public int X { get; set; }
}
public class Derived : Base
{
    public int Y { get; set; }
}

此配置启用多态序列化 Base ,特别是当运行时类型为 Derived:

Base value = new Derived();
JsonSerializer.Serialize<Base>(value); // { "X" : 0, "Y" : 0 }

请注意,这不会启用多态 反序列 化,因为有效负载将作为 Base:

Base value = JsonSerializer.Deserialize<Base>(@"{ ""X"" : 0, ""Y"" : 0 }");
value is Derived; // false

查看 .NET 7 中 System.Text.Json 的新增功能 博客文章,了解有关类型层次结构的更多详细信息。

.NET SDK

.NET SDK 继续添加新功能,让您比以往任何时候都更有效率。 在 .NET 7 中,我们改进了您使用 .NET CLI、创作模板和在中央位置管理包的体验。

CLI 解析器和选项卡完成

这 dotnet newcommand 已经为用户熟悉和喜爱的许多子命令提供了更加一致和直观的界面。 还支持模板选项和参数的制表符完成。 现在,CLI 在用户键入时提供有关有效参数和选项的反馈。

以下是新的帮助输出示例:

❯ dotnet new --help
Description:
  Template Instantiation Commands for .NET CLI.
Usage:
  dotnet new [<template-short-name> [<template-args>...]] [options]
  dotnet new [command] [options]
Arguments:
  <template-short-name>  A short name of the template to create.
  <template-args>        Template specific options to use.
Options:
  -?, -h, --help  Show command line help.
Commands:
  install <package>       Installs a template package.
  uninstall <package>     Uninstalls a template package.
  update                  Checks the currently installed template packages for update, and install the updates.
  search <template-name>  Searches for the templates on NuGet.org.
  list <template-name>    Lists templates containing the specified template name. If no name is specified, lists all templates.

这 dotnet CLI 很长一段时间以来,它一直支持使用流行的 shell,如 PowerShell、bash、zsh 和 fish 等等。 然而,实现有意义的补全取决于单独的 dotnet 命令。 对于 .NET 7, dotnet new命令学习了如何提供制表符补全。

❯ dotnet new angular
angular              grpc                 razor                viewstart            worker               -h
blazorserver         mstest               razorclasslib        web                  wpf                  /?
blazorwasm           mvc                  razorcomponent       webapi               wpfcustomcontrollib  /h
classlib             nugetconfig          react                webapp               wpflib               install
console              nunit                reactredux           webconfig            wpfusercontrollib    list
editorconfig         nunit-test           sln                  winforms             xunit                search
gitignore            page                 tool-manifest        winformscontrollib   --help               uninstall
globaljson           proto                viewimports          winformslib          -?                   update

这有助于您在创建新的 .NET 项目时做出选择,以了解哪些选项和参数可供您使用。

❯ dotnet new web --dry-run
--dry-run                  --language                 --output                   -lang
--exclude-launch-settings  --name                     --type                     -n
--force                    --no-https                 -?                         -o
--framework                --no-restore               -f                         /?
--help                     --no-update-check          -h                         /h

此外,给定命令通常错误或不支持哪些常见选项和参数。 相反,您只会看到当前版本的 .NET CLI 支持的内容。

❯ dotnet new blazorserver --auth Individual
Individual     IndividualB2C  MultiOrg       None           SingleOrg      Windows

模板创作

.NET 7 将约束的概念添加到 .NET 模板。 约束允许您定义允许模板的上下文,这有助于模板引擎确定应在 dotnet new list 等命令中显示哪些模板。 在此版本中,我们添加了对三种约束的支持:

  • 操作系统: 根据用户的操作系统限制模板
  • 模板引擎主机: 根据执行模板引擎的主机来限制模板。 这通常是 .NET CLI 本身,或者是嵌入式场景,如 Visual Studio 或 Visual Studio for Mac 中的“新建项目”对话框。
  • 已安装的工作负载: 要求在模板可用之前安装指定的 .NET SDK 工作负载。

在所有情况下,描述这些约束就像在模板的配置文件中添加一个新的约束部分一样简单:

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

我们还添加了选择参数的新功能。 这是用户在单个选择中指定多个值的能力。 这可以以与使用标志样式枚举相同的方式使用。 此类参数的常见示例可能是:

  • 在 Web 模板上选择多种形式的身份验证。
  • 在 MAUI 模板中一次选择多个目标平台(iOS、Android、Web)。

选择加入此行为就像添加一样简单 "allowMultipleValues": true到模板配置中的参数定义。 完成后,您将可以访问在模板内容中使用的多个辅助函数,以帮助检测用户选择的特定值。

中央包管理

依赖管理是 NuGet 的核心功能。 管理单个项目的依赖关系很容易。 管理多项目解决方案的依赖关系可能会变得很困难,因为它们的规模和复杂性开始扩大。 在您管理许多不同项目的公共依赖项的情况下,您可以利用 NuGet 的中央包管理功能从一个位置轻松完成所有这些工作。

要开始使用中央包管理,您可以创建一个 Directory.Packages.props解决方案根目录下的文件并设置 MSBuild 属性 ManagePackageVersionsCentrally为真。

在内部,您可以定义解决方案所需的每个相应的包版本,使用 <PackageVersion />定义包 ID 和版本的元素。

<Project>
  <PropertyGroup>
    <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
  </PropertyGroup>
  <ItemGroup>
    <PackageVersion Include="Newtonsoft.Json" Version="13.0.1" />
  </ItemGroup>
</Project>

在解决方案的项目中,您可以使用相应的 <PackageReference />您知道并喜欢的语法,但没有 Version 属性来推断集中管理的版本。

<Project Sdk="Microsoft.NET.Sdk"><
>>
  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Newtonsoft.Json" />
  </ItemGroup>
</Project>

表现

性能一直是每个 .NET 版本的重要组成部分。 每年,.NET 团队都会发布一篇关于最新改进的博客。 这是最新性能帖子的简短介绍:

TL;DR:.NET 7 速度很快。 真的很快。 在这个版本中,一千个影响性能的 PR 进入了运行时和核心库,更不用说 ASP.NET Core 和 Windows Forms 和 Entity Framework 及其他方面的所有改进。 这是有史以来最快的 .NET。 如果你的经理问你为什么你的项目应该升级到 .NET 7,你可以说“除了发行版中的所有新功能之外,.NET 7 超级快”。 —— 斯蒂芬·图布

下面是对 JIT 编译器的一些性能改进的简短总结。 如果您想全面深入了解所有更改,请查看 .NET 7 中的性能改进

堆栈更换 (OSR)

堆栈上替换 (OSR) 允许运行时在方法执行过程中更改当前正在运行的方法执行的代码,尽管这些方法在“堆栈上”处于活动状态。 它作为分层编译的补充。

OSR 允许长时间运行的方法在执行过程中切换到更优化的版本,因此运行时可以首先快速 JIT 所有方法,然后在通过分层编译频繁调用这些方法或通过长时间运行循环时过渡到更优化的版本操作系统。

OSR 提高了启动时间。 几乎所有的方法现在最初都是由快速 JIT 进行的。 我们已经看到像 Avalonia “IL” spy 这样的 jitting-heavy 应用程序的启动时间缩短了 25%,我们跟踪的各种 TechEmpower 基准测试显示,首次请求的时间缩短了 10-30%。

OSR 还可以提高应用程序的性能,尤其是使用动态 PGO 的应用程序,因为现在可以更好地优化带有循环的方法。 例如, Array2启用 OSR 后,微基准测试显示出显着的改进。

配置文件引导优化 (PGO)

Profile-Guided Optimization (PGO) 在多种语言和编译器中已经存在了很长时间。 基本思想是编译应用程序,要求编译器将检测注入应用程序以跟踪各种有趣的信息。 然后,你让你的应用程序通过它的步伐,运行各种常见的场景,使该仪器“分析”应用程序执行时发生的情况,然后保存结果。 然后重新编译应用程序,将这些检测结果反馈给编译器,并允许它优化应用程序以准确地预期它的使用方式。

这种 PGO 方法被称为“静态 PGO”,因为所有信息都是在实际部署之前收集的,这是 .NET 多年来以各种形式所做的事情。 .NET 中有趣的发展是“动态 PGO”,它是在 .NET 6 中引入的,但默认情况下是关闭的。

动态 PGO 利用分层编译。 JIT 检测第 0 层代码以跟踪方法被调用的次数,或者在循环的情况下,循环执行了多少次。 分层编译可以提供多种可能性。 例如,它可以准确地跟踪哪些具体类型被用作接口调度的目标,然后在第 1 层中,专门化代码以期望最常见的类型(这被称为“受保护的去虚拟化”或 GDV )。 你可以在这个小例子中看到这一点。 设置 DOTNET_TieredPGO环境变量为 1,然后在 .NET 7 上运行它:

class Program
{
  static void Main()
  {
    IPrinter printer = new Printer();
    for (int i = 0; ; i++)
    {
      DoWork(printer, i);
    }
  }
  static void DoWork(IPrinter printer, int i)
  {
    printer.PrintIfTrue(I == int.MaxValue);
  }
  interface iPrinter
  {
    void PrintIfTrue(bool condition);
  }
  class Printer : iPrinter
  {
    public void PrintIfTrue(bool condition)
    {
      if (condition) Console.WriteLine“"Print”");
    }
  }
}

使用 PGO 获得的主要改进是它现在可以与 .NET 7 中的 OSR 一起使用。这意味着执行接口调度的热运行方法可以获得这些去虚拟化/内联优化。

禁用 PGO 后,您可以获得相同的 .NET 6 和 .NET 7 性能吞吐量。

方法 运行 意思是 比率 代表PGO .NET 6.0 1.665 纳秒 1.00 代表PGO .NET 7.0 1.659 纳秒 1.00

但是当您通过 .csproj 在 .csproj 中启用动态 PGO 时,情况会发生变化 <TieredPGO>true</TieredPGO>或环境变量 DOTNET_TieredPGO=1. .NET 6 的速度提高了约 14%,但 .NET 7 的速度提高了约 3 倍。

方法 运行 意思是 比率 代表PGO .NET 6.0 1.427.7 纳秒 1.00 代表PGO .NET 7.0 539.0 0.38

原生 AOT

对许多人来说,软件上下文中的“性能”一词与吞吐量有关。 某件事的执行速度有多快? 它每秒可以处理多少数据? 它每秒可以处理多少个请求? 等等。 但性能还有许多其他方面。 它消耗多少内存? 它启动并达到做有用的事情的速度有多快? 它在磁盘上消耗多少空间? 下载需要多长时间?

然后是相关的担忧。 要实现这些目标,需要哪些依赖项? 它需要执行哪些类型的操作来实现这些目标,以及所有这些操作在目标环境中是否允许? 如果本段中的任何一段与您产生共鸣,那么您就是现在在 .NET 7 中提供的本机 AOT 支持的目标受众。

.NET 长期以来一直支持 AOT 代码生成。 例如,.NET Framework 有它的形式 ngen,而 .NET Core 的形式为 crossgen. 这两种解决方案都涉及一个标准的 .NET 可执行文件,它的一些 IL 已经编译为汇编代码,但并非所有方法都会为它们生成汇编代码,各种事情都可能使生成的汇编代码无效,外部 .NET 程序集没有可以加载任何本机汇编代码,依此类推,在所有这些情况下,运行时继续使用 JIT 编译器。 原生 AOT 不同。 它是 CoreRT 的演变,它本身是 .NET Native 的演变,并且完全没有 JIT。

发布构建生成的二进制文件是目标平台特定于平台的文件格式(例如,Windows 上的 COFF、Linux 上的 ELF、macOS 上的 Mach-O)的完全独立的可执行文件,除了标准之外没有任何外部依赖项到该平台(例如,libc)。 而且它完全是原生的:看不到 IL,没有 JIT,什么都没有。 所有必需的代码都被编译和/或链接到可执行文件中,包括与标准 .NET 应用程序和服务一起使用的相同 GC,以及围绕线程等提供服务的最小运行时。

所有这些都带来了巨大的好处:超快的启动时间、小型且完全自包含的部署,以及在不允许 JIT 编译器的地方运行的能力(因为可写的内存页面不能执行)。 它也带来了限制:没有 JIT 意味着没有动态加载任意程序集(例如, Assembly.LoadFile)并且没有反射发射(例如, DynamicMethod),并且所有内容都已编译并链接到应用程序中,这意味着使用(或可能使用)更多功能并且您的部署可以更大。 即使有这些限制,对于特定类别的应用程序,Native AOT 仍然是 .NET 7 中令人难以置信的令人兴奋和受欢迎的补充。

今天,Native AOT 专注于控制台应用,所以我们来创建一个控制台应用:

dotnet new console -o nativeaotexample

您现在有一个“Hello World”控制台应用程序。 要启用使用本机 AOT 发布应用程序,请编辑 .csproj 以将以下内容包含在现有的 <PropertyGroup>:

<PublishAot>true</PublishAot>

该应用程序现在已完全配置为能够以本机 AOT 为目标。 剩下的就是发布了。 如果您想发布到 Windows x64运行时,您可以使用以下命令:

dotnet publish -r win-x64 -c Release

这会在输出发布目录中生成一个可执行文件:

    Directory: C:\nativeaotexample\bin\Release\net7.0\win-x64\publish<
>>
Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---           8/27/2022  6:18 PM        3648512 nativeaotexample.exe
-a---           8/27/2022  6:18 PM       14290944 nativeaotexample.pdb

大约 3.5MB 的 .exe 是可执行文件,它旁边的 .pdb 是调试信息,部署应用程序不需要这些信息。 你现在可以复制那个 nativeaotexample.exe到任何 64 位 Windows 机器,无论盒子上的任何位置可能安装或未安装什么 .NET,应用程序都会运行。

支持 .NET 7

.NET 7 由 Microsoft 正式支持。 它被标记为 标准期限支持 (STS) 版本,将支持 18 个月。 奇数 .NET 版本是 STS 版本,在随后的 STS 或 LTS 版本发布后六个月内获得免费支持和补丁。 有关更多详细信息,请参阅我们的 .NET 和 .NET Core 支持生命周期 文档。

反馈

帮助我们塑造 .NET 8 和未来版本。 通过在 GitHub 上支持问题 在开发者社区提供反馈 以及与社区中的其他人分享您的 .NET 应用程序和项目,

分享您的想法并随时了解有关 .NET 7 的最新消息 通过#dotnet7

.NET 会议 2022

请务必收听 .NET Conf 2022 。 这是一个为期三天的免费虚拟开发者活动,旨在庆祝 .NET 的主要版本。 从今天(11 月 8 日到 11 月 10 日)加入我们,直接从 Microsoft 的产品团队和发布背后的 .NET 社区了解 .NET 7。 .NET Conf 包含超过 80 个会话,涵盖了无数新功能,这些新功能可以增强您使用 .NET 7 构建的应用程序的能力。

立即下载 .NET 7

我们只想说:

没有社区,.NET 就无法存在。 .NET 项目是一个通过每个人的独特和创造性贡献的项目。 这些伟大的成就和慷慨来自对我们周围人的支持和关心。 感谢您的参与、您的分享以及您对 .NET 社区的归属。

.NET 7 是一个主要版本,它通过改进性能、功能和可用性等基础知识来提高开发人员的生活质量。 我们希望这些新功能和新增功能继续让您对 .NET 平台的使用感到满意。

你在等什么? 准备好了。 下载 .NET 7。 

© GVGNN 2013-2026