欢送应用 .NET 6。明天的版本是.NET 团队和社区一年多致力的后果。C# 10 和 F# 6 提供了语言改良,使您的代码更简略、更好。性能大幅晋升,咱们曾经看到微软升高了托管云服务的老本。.NET 6 是第一个原生反对 Apple Silicon (Arm64) 的版本,并且还针对 Windows Arm64 进行了改良。咱们构建了一个新的动静配置文件疏导优化 (PGO) 零碎,该零碎可提供仅在运行时才可能进行的深度优化。应用 dotnet monitor 和 OpenTelemetry 改良了云诊断。WebAssembly 反对更有能力和性能。为 HTTP/ 3 增加了新的 API,解决 JSON、数学和间接操作内存。.NET 6 将反对三年。开发人员曾经开始将应用程序降级到 .NET 6,咱们在生产中听到了很好的晚期成绩。.NET 6 已为您的应用程序做好筹备。
您能够下载实用于 Linux、macOS 和 Windows 的.NET 6。
- 安装程序和二进制文件
- 容器图像
- Linux 软件包
- 发行阐明
- API 差别
- 已知的问题
- GitHub 问题跟踪器
请参阅 ASP.NET Core、Entity Framework、Windows Forms、.NET MAUI、YARP 和 dotnet 监视器帖子,理解各种场景中的新增性能。
.NET 6 亮点
.NET 6 是:
- 应用 Microsoft 服务、其余公司运行的云应用程序和开源我的项目进行生产压力测试。
- 作为最新的长期反对 (LTS) 版本反对三年。
- 跨浏览器、云、桌面、物联网和挪动应用程序的对立平台,所有这些都应用雷同的 .NET 库并可能轻松共享代码。
- 性能失去了极大的进步,尤其是文件 I/O,这独特导致了执行工夫、提早和内存应用的缩小。
- C# 10 提供了语言改良,例如记录构造、隐式应用和新的 lambda 性能,而编译器增加了增量源生成器。F# 6 增加了新性能,包含基于工作的异步、管道调试和泛滥性能改良。
- Visual Basic 在 Visual Studio 体验和 Windows 窗体我的项目关上体验方面有所改进。
- Hot Reload 使您可能跳过从新构建和重新启动您的应用程序以查看新更改(在您的利用程序运行时),Visual Studio 2022 和 .NET CLI 反对 C# 和 Visual Basic。
- 云诊断已通过 OpenTelemetry 和 dotnet monitor 失去改良,当初在生产中反对并在 Azure 应用服务中可用。
- JSON API 的性能更弱小,并且通过序列化程序的源生成器具备更高的性能。
- ASP.NET Core 中引入的最小 API 可简化入门体验并进步 HTTP 服务的性能。
- Blazor 组件当初能够从 JavaScript 出现并与现有的基于 JavaScript 的应用程序集成。
- Blazor WebAssembly (Wasm) 应用程序的 WebAssembly AOT 编译,以及对运行时从新链接和本机依赖项的反对。
- 应用 ASP.NET Core 构建的单页应用程序当初应用更灵便的模式,可与 Angular、React 和其余风行的前端 JavaScript 框架一起应用。
- 增加了 HTTP/3,以便 ASP.NET Core、HttpClient 和 gRPC 都能够与 HTTP/3 客户端和服务器交互。
- File IO 当初反对符号链接,并且通过 re-written-from-scratch 大大提高了性能 FileStream。
- 通过反对 OpenSSL 3、ChaCha20Poly1305 加密计划和运行时深度进攻缓解,特地是 W^X 和 CET,安全性失去了改良。
- 能够为 Linux、macOS 和 Windows(以前只有 Linux)公布单文件应用程序(免提取)。
- IL 修整当初更加弱小和无效,新的正告和分析器可确保正确的最终后果。
- 增加了源生成器和分析器,可帮忙您生成更好、更平安和更高性能的代码。
- 源代码构建使 Red Hat 等组织可能从源代码构建 .NET 并向其用户提供本人的构建。
该版本包含大概一万次 git 提交。即便这篇文章很长,它也跳过了许多改良。您必须下载并试用 .NET 6 能力看到所有新性能。
反对
.NET 6 是一个长期反对 (LTS) 版本,将反对三年。它反对多种操作系统,包含 macOS Apple Silicon 和 Windows Arm64。
Red Hat 与 .NET 团队单干,在 Red Hat Enterprise Linux 上反对 .NET。在 RHEL 8 及更高版本上,.NET 6 将可用于 AMD 和 Intel (x64_64)、ARM (aarch64) 以及 IBM Z 和 LinuxONE (s390x) 架构。
请开始将您的应用程序迁徙到 .NET 6,尤其是 .NET 5 应用程序。咱们从晚期采纳者那里据说,从 .NET Core 3.1 和 .NET 5 降级到 .NET 6 很简略。
Visual Studio 2022 和 Visual Studio 2022 for Mac 反对 .NET 6。Visual Studio 2019、Visual Studio for Mac 8 或 MSBuild 16 不反对它。如果要应用 .NET 6,则须要降级到 Visual Studio 2022(当初也是 64 位)。Visual Studio Code C# 扩大反对 .NET 6。
Azure App 服务:
Azure Functions 当初反对在 .NET 6 中运行无服务器函数。
App Service .NET 6 GA Announcement 为 ASP.NET Core 开发人员提供了信息和详细信息,他们很快乐明天开始应用 .NET 6。
Azure 动态 Web 利用当初反对带有 Blazor WebAssembly 前端和 Azure Function API 的全栈 .NET 6 应用程序。
留神:如果您的利用曾经在应用服务上运行 .NET 6 预览版或 RC 版本,则在将 .NET 6 运行时和 SDK 部署到您所在区域后,它将在第一次重新启动时自动更新。如果您部署了一个独立的应用程序,您将须要从新构建和重新部署。
对立扩大平台
.NET 6 为浏览器、云、桌面、物联网和挪动应用程序提供了一个对立的平台。底层平台已更新,可满足所有利用类型的需要,并便于在所有利用中重用代码。新性能和改良同时实用于所有应用程序,因而您在云或挪动设施上运行的代码的行为形式雷同并具备雷同的劣势。
.NET 开发人员的范畴随着每个版本的公布而不断扩大。机器学习和 WebAssembly 是最近增加的两个。例如,通过机器学习,您能够编写在流数据中查找异样的应用程序。应用 WebAssembly,您能够在浏览器中托管 .NET 应用程序,就像 HTML 和 JavaScript 一样,或者将它们与 HTML 和 JavaScript 混合应用。
最令人兴奋的新增性能之一是.NET Multi-platform App UI (.NET MAUI)。您当初能够在单个我的项目中编写代码,从而跨桌面和挪动操作系统提供古代客户端应用程序体验。.NET MAUI 将比 .NET 6 稍晚公布。咱们在 .NET MAUI 上投入了大量工夫和精力,很快乐可能公布它并看到 .NET MAUI 应用程序投入生产。
当然,.NET 应用程序也能够在家中应用 Windows 桌面(应用 Windows Forms 和 WPF)以及应用 ASP.NET Core 在云中。它们是咱们提供工夫最长的应用程序类型,并且依然十分受欢迎,咱们在 .NET 6 中对其进行了改良。
面向 .NET 6
持续以宽泛平台为主题,在所有这些操作系统上编写 .NET 代码很容易。
要以 .NET 6 为指标,您须要应用 .NET 6 指标框架,如下所示:
<TargetFramework>net6.0</TargetFramework>
net6.0 Target Framework Moniker (TFM) 使您能够拜访 .NET 提供的所有跨平台 API。如果您正在编写控制台应用程序、ASP.NET Core 应用程序或可重用的跨平台库,这是最佳抉择。
如果您针对特定操作系统(例如编写 Windows 窗体或 iOS 应用程序),那么还有另一组 TFM(每个都针对显而易见的操作系统)供您应用。它们使您能够拜访所有 net6.0 的 API 以及一堆特定于操作系统的 API。
- net6.0-android
- net6.0-ios
- net6.0-maccatalyst
- net6.0-tvos
- net6.0-windows
每个无版本 TFM 都相当于针对 .NET 6 反对的最低操作系统版本。如果您想要具体或拜访更新的 API,能够指定操作系统版本。
net6.0 和 net6.0-windows TFMs 都反对(与 .NET 5 雷同)。Android 和 Apple TFM 是 .NET 6 的新性能,目前处于预览阶段。稍后的 .NET 6 更新将反对它们。
操作系统特定的 TFM 之间没有兼容性关系。例如,net6.0-ios 与 net6.0-tvos 不兼容。如果您想共享代码,您须要应用带有 #if 语句的源代码或带有 net6.0 指标代码的二进制文件来实现。
性能
自从咱们启动 .NET Core 我的项目以来,该团队始终在一直地关注性能。Stephen Toub 在记录每个版本的 .NET 性能停顿方面做得十分杰出。欢送查看在 .NET 6 中的性能改良的帖子。在这篇文章中,外面包含您想理解的重大性能改良,包含文件 IO、接口转换、PGO 和 System.Text.Json。
动静 PGO
动静轮廓疏导优化 (PGO)能够显着进步稳态性能。例如,PGO 为 TechEmpower JSON“MVC”套件的每秒申请数进步了 26%(510K -> 640K)。
动静 PGO 建设在分层编译的根底上,它使办法可能首先十分疾速地编译(称为“第 0 层”)以进步启动性能,而后在启用大量优化的状况下随后从新编译(称为“第 1 层”)一旦该办法被证实是有影响的。该模型使办法可能在第 0 层中进行检测,以容许对代码的执行进行各种察看。在第 1 层从新调整这些办法时,从第 0 层执行收集的信息用于更好地优化第 1 层代码。这就是机制的实质。
动静 PGO 的启动工夫将比默认运行时稍慢,因为在第 0 层办法中运行了额定的代码来察看办法行为。
要启用动静 PGO,请在应用程序将运行的环境中设置 DOTNET_TieredPGO=1。您还必须确保启用分层编译(默认状况下)。动静 PGO 是可选的,因为它是一种新的且有影响力的技术。咱们心愿公布抉择退出应用和相干反馈,以确保它通过全面压力测试。咱们对分层编译做了同样的事件。至多一个十分大的 Microsoft 服务反对并已在生产中应用动静 PGO。咱们激励您尝试一下。
您能够在.NET 6 中的性能帖子中看到更多对于动静 PGO 劣势的信息,包含以下微基准,它测量特定 LINQ 枚举器的老本。
private IEnumerator<long> _source = Enumerable.Range(0, long.MaxValue).GetEnumerator();
[Benchmark]
public void MoveNext() => _source.MoveNext();
这是有和没有动静 PGO 的后果。
这是一个相当大的差别,但代码大小也有所增加,这可能会让一些读者感到诧异。这是由 JIT 生成的汇编代码的大小,而不是内存调配(这是一个更常见的焦点)。.NET 6 性能帖子对此有很好的解释。
PGO 实现中常见的一种优化是“热 / 冷拆散”,其中常常执行的办法局部(“热”)在办法开始时凑近在一起,而不常常执行的办法局部(“冷”)是移到办法的开端。这样能够更好地应用指令缓存,并最大限度地缩小可能未应用的代码负载。
作为上下文,接口调度是 .NET 中最低廉的调用类型。非虚构办法调用是最快的,甚至更快的是能够通过内联打消的调用。在这种状况下,动静 PGO 为 MoveNext 提供了两个(代替)调用站点。第一个 – 热的 – 是对 Enumerable+RangeIterator.MoveNext 的间接调用,另一个 – 冷的 – 是通过 IEnumerator<int> 的虚构接口调用。如果大多数时候最热门的人都被叫到,那将是一个微小的胜利。
这就是魔法。当 JIT 检测此办法的第 0 层代码时,包含检测此接口调度以跟踪每次调用时 _source 的具体类型。JIT 发现每次调用都在一个名为 Enumerable+RangeIterator 的类型上,这是一个公有类,用于在 Enumerable 实现外部实现 Enumerable.Range。因而,对于第 1 层,JIT 已收回查看以查看 _source 的类型是否为 Enumerable+RangeIterator:如果不是,则跳转到咱们之前强调的执行失常接口调度的冷局部。但如果是 – 基于剖析数据,预计绝大多数工夫都是这种状况 – 而后它能够持续间接调用非虚拟化的 Enumerable+RangeIterator.MoveNext 办法。不仅如此,它还认为内联 MoveNext 办法是有利可图的。最终成果是生成的汇编代码有点大,但针对预期最常见的确切场景进行了优化。当咱们开始构建动静 PGO 时,这些就是咱们想要的那种胜利。
动静 PGO 将在 RyuJIT 局部再次探讨。
文件 IO 改良
FileStream 简直齐全用 .NET 6 重写,重点是进步异步文件 IO 性能。在 Windows 上,实现不再应用阻塞 API,并且能够快几倍!咱们还改良了所有平台上的内存应用。在第一次异步操作(通常调配)之后,咱们曾经使异步操作免调配!此外,咱们曾经使 Windows 和 Unix 实现不同的边缘状况的行为对立(这是可能的)。
这种重写的性能改良使所有操作系统受害。对 Windows 的益处是最大的,因为它远远落后。macOS 和 Linux 用户也应该会看到显着 FileStream 的性能改良。
以下基准将 100 MB 写入新文件。
private byte[] _bytes = new byte[8_000]; [Benchmark] public async Task Write100MBAsync() { using FileStream fs = new("file.txt", FileMode.Create, FileAccess.Write, FileShare.None, 1, FileOptions.Asynchronous); for (int i = 0; i < 100_000_000 / 8_000; i++) await fs.WriteAsync(_bytes); }
在带有 SSD 驱动器的 Windows 上,咱们察看到 4 倍的减速和超过 1200 倍的调配降落:
咱们还意识到须要更高性能的文件 IO 性能:并发读取和写入,以及扩散 / 收集 IO。针对这些状况,咱们为 System.IO.File 和 System.IO.RandomAccess 类引入了新的 API。
async Task AllOrNothingAsync(string path, IReadOnlyList<ReadOnlyMemory<byte>> buffers)
{
using SafeFileHandle handle = File.OpenHandle(
path, FileMode.Create, FileAccess.Write, FileShare.None, FileOptions.Asynchronous,
preallocationSize: buffers.Sum(buffer => buffer.Length)); // hint for the OS to pre-allocate disk space
await RandomAccess.WriteAsync(handle, buffers, fileOffset: 0); // on Linux it's translated to a single sys-call!
}
该示例演示:
- 应用新 File.OpenHandle API 关上文件句柄。
- 应用新的预调配大小性能预调配磁盘空间。
- 应用新的 Scatter/Gather IO API 写入文件。
预调配大小性能进步了性能,因为写入操作不须要扩大文件,并且文件不太可能被碎片化。这种办法进步了可靠性,因为写入操作将不再因空间有余而失败,因为空间已被保留。Scatter/Gather IO API 缩小了写入数据所需的零碎调用次数。
更快的接口检查和转换
界面铸造性能进步了 16% – 38%。这种改良对于 C# 与接口之间的模式匹配特地有用。
这张图表展现了一个有代表性的基准测试的改良规模。
将 .NET 运行时的一部分从 C++ 迁徙到托管 C# 的最大劣势之一是它升高了奉献的阻碍。这包含接口转换,它作为晚期的 .NET 6 更改移至 C#。.NET 生态系统中懂 C# 的人比懂 C++ 的人多(而且运行时应用具备挑战性的 C++ 模式)。仅仅可能浏览形成运行时的一些代码是造就以各种模式做出奉献的信念的重要一步。
归功于 Ben Adams。
System.Text.Json 源生成器
咱们为 System.Text.Json 增加了一个源代码生成器,它防止了在运行时进行反射和代码生成的须要,并且能够在构建时生成最佳序列化代码。序列化程序通常应用十分激进的技术编写,因为它们必须如此。然而,如果您浏览本人的序列化源代码(应用序列化程序),您能够看到显著的抉择应该是什么,能够使序列化程序在您的特定状况下更加优化。这正是这个新的源生成器所做的。除了进步性能和缩小内存之外,源代码生成器还生成最适宜拆卸修整的代码。这有助于制作更小的应用程序。
序列化 POCO 是一种十分常见的场景。应用新的源代码生成器,咱们察看到序列化速度比咱们的基准快 1.6 倍。
TechEmpower 缓存基准测试平台或框架对来自数据库的信息进行内存缓存。基准测试的 .NET 实现执行缓存数据的 JSON 序列化,以便将其作为响应发送到测试工具。
咱们察看到约 100K RPS 增益(减少约 40%)。与 MemoryCache 性能改良相结合时,.NET 6 的吞吐量比 .NET 5 高 50%!
C# 10
欢送来到 C# 10。C# 10 的一个次要主题是持续从 C# 9 中的顶级语句开始的简化之旅。新性能从 Program.cs 中删除了更多的典礼,导致程序只有一行。他们的灵感来自于与没有 C# 教训的人(学生、业余开发人员和其他人)交谈,并理解什么对他们来说最无效且最直观。
大多数.NET SDK 模板都已更新,以提供当初能够应用 C# 10 实现的更简略、更简洁的体验。咱们收到反馈说,有些人不喜爱新模板,因为它们不适宜专家,删除面向对象,删除在编写 C# 的第一天学习的重要概念,或激励在一个文件中编写整个程序。主观地说,这些观点都不正确。新模型同样实用于作为业余开发人员的学生。然而,它与 .NET 6 之前的 C 派生模型不同。
C# 10 中还有其余一些性能和改良,包含记录构造。
全局应用指令
全局 using 指令让您 using 只需指定一次指令并将其利用于您编译的每个文件。
以下示例显示了语法的广度:
- global using System;
- global using static System.Console;
- global using Env = System.Environment;
您能够将 global using 语句放在任何 .cs 文件中,包含在 Program.cs 中。
隐式 usings 是一个 MSBuild 概念,它会依据 SDK 主动增加一组指令。例如,控制台应用程序隐式应用不同于 ASP.NET Core。
隐式应用是可选的,并在 a 中启用 PropertyGroup:
<ImplicitUsings>enable</ImplicitUsings>
隐式应用对于现有我的项目是可选的,但默认蕴含在新 C# 我的项目中。无关详细信息,请参阅隐式应用。
文件范畴的命名空间
文件范畴的命名空间使您可能申明整个文件的命名空间,而无需将残余内容嵌套在 {…} 中. 只容许一个,并且必须在申明任何类型之前呈现。
新语法是单个的一行:
namespace MyNamespace;
class MyClass {…} // Not indented
这种新语法是三行缩进款式的代替计划:
namespace MyNamespace
{
class MyClass {…} // Everything is indented
}
益处是在整个文件位于同一个命名空间中的极其常见的状况下缩小缩进。
记录构造
C# 9 将记录作为一种非凡的面向值的类模式引入。在 C# 10 中,您还能够申明构造记录。C# 中的构造曾经具备值相等,但记录构造增加了 == 运算符和 IEquatable<T> 的实现,以及基于值的 ToString 实现:
public record struct Person {public string FirstName { get; init;} public string LastName {get; init;} }
就像记录类一样,记录构造能够是“地位的”,这意味着它们有一个主构造函数,它隐式申明与参数对应的公共成员:
public record struct Person(string FirstName, string LastName);
然而,与记录类不同,隐式公共成员是可变的主动实现的属性。这样一来,记录构造就成为了元组的天然成长故事。例如,如果您有一个返回类型(string FirstName, string LastName),并且您心愿将其扩大为命名类型,您能够轻松地申明相应的地位构造记录并保护可变语义。
如果你想要一个具备只读属性的不可变记录,你能够申明整个记录构造 readonly(就像你能够其余构造一样):
public readonly record struct Person(string FirstName, string LastName);
C# 10 不仅反对记录构造,还反对所有构造以及匿名类型的 with 表达式:
var updatedPerson = person with {FirstName = “Mary”};
F# 6
F# 6 旨在让 F# 更简略、更高效。这实用于语言设计、库和工具。咱们对 F# 6(及更高版本)的指标是打消语言中让用户感到诧异或妨碍学习 F# 的极其状况。咱们很快乐能与 F# 社区单干进行这项继续的致力。
让 F# 更快、更互操作
新语法 task {…}间接创立一个工作并启动它。这是 F# 6 中最重要的性能之一,它使异步工作更简略、性能更高,并且与 C# 和其余 .NET 语言的互操作性更强。以前,创立 .NET 工作须要应用 async {…}来创立工作并调用 Async.StartImmediateAsTask。
该性能 task {…}建设在称为“可复原代码”RFC FS-1087 的根底之上。可复原代码是一个外围个性,咱们心愿在将来应用它来构建其余高性能异步和屈从状态机。
F# 6 还为库作者增加了其余性能个性,包含 InlineIfLambda 和 F# 流动模式的未装箱示意。一个特地显着的性能改良在于列表和数组表达式的编译,当初它们的速度进步了 4 倍,并且调试也更好、更简略。
让 F# 更易学、更对立
F# 6 启用 expr[idx]索引语法。到目前为止,F# 始终应用 expr.[idx] 进行索引。删除点符号是基于第一次应用 F# 用户的重复反馈,点的应用与他们冀望的规范实际有不必要的差别。在新代码中,咱们倡议系统地应用新的 expr[idx]索引语法。作为一个社区,咱们都应该切换到这种语法。
F# 社区为使 F# 语言在 F# 6 中更加对立做出了重要改良。其中最重要的是打消了 F# 缩进规定中的一些不统一和限度。使 F# 更加对立的其余设计增加包含增加 as 图案;在计算表达式中容许“重载自定义操作”(对 DSL 有用);容许_抛弃 use 绑定并容许 %B 在输入中进行二进制格式化。F# 外围库增加了用于复制和更新列表、数组和序列的新函数,以及其余 NativePtr 外在函数。自 2.0 起弃用的 F# 的一些旧性能当初会导致谬误。其中许多更改更好地使 F# 与您的冀望保持一致,从而缩小意外。
F# 6 还减少了对 F# 中其余“隐式”和“类型导向”转换的反对。这意味着更少的显式向上转换,并为 .NET 款式的隐式转换增加了一流的反对。F# 也进行了调整,以更好地适应应用 64 位整数的数字库时代,并隐式扩大了 32 位整数。
改良 F# 工具
F# 6 中的工具改良使日常编码更容易。新的“管道调试”容许您单步执行、设置断点并查看 F# 管道语法 input |> f1 |> f2 的两头值。暗影值的调试显示已失去改良,打消了调试时常见的混同源。F# 工具当初也更高效,F# 编译器并行执行解析阶段。F# IDE 工具也失去了改良。F# 脚本当初更加强壮,容许您通过 global.json 文件固定应用的 .NET SDK 版本。
热重载
Hot Reload 是另一个性能个性,专一于开发人员的生产力。它使您可能对正在运行的应用程序进行各种代码编辑,从而缩短您期待应用程序从新构建、重新启动或从新导航到您在进行代码更改后所在位置所需的工夫。
Hot Reload 可通过 dotnet watch CLI 工具和 Visual Studio 2022 应用。您能够将 Hot Reload 与多种利用类型一起应用,例如 ASP.NET Core、Blazor、.NET MAUI、控制台、Windows 窗体 (WinForms)、WPF、WinUI 3、Azure 函数等。
应用 CLI 时,只需应用 启动您的 .NET 6 应用程序 dotnet watch,进行任何受反对的编辑,而后在保留文件时(如在 Visual Studio Code 中),这些更改将立刻利用。如果不反对更改,详细信息将记录到命令窗口。
此图像显示了一个应用 dotnet watch. 我对.cs 文件和.cshtml 文件进行了编辑(如日志中所述),两者都利用于代码并在不到半秒的工夫内十分疾速地反映在浏览器中。
应用 Visual Studio 2022 时,只需启动您的应用程序,进行反对的更改,而后应用新的“热重载”按钮(如下图所示)利用这些更改。您还能够通过同一按钮上的下拉菜单抉择在保留时利用更改。应用 Visual Studio 2022 时,热重载可用于多个 .NET 版本,实用于 .NET 5+、.NET Core 和 .NET Framework。例如,您将可能对按钮的 OnClickEvent 处理程序进行代码暗藏更改。应用程序的 Main 办法不反对它。
留神:RuntimeInformation.FrameworkDescription 中存在一个谬误,该谬误将在该图像中展现,很快就会修复。
Hot Reload 还与现有的 Edit and Continue 性能(在断点处进行时)以及用于实时编辑应用程序 UI 的 XAML Hot Reload 协同工作。目前反对 C# 和 Visual Basic 应用程序(不是 F#)。
平安
.NET 6 中的安全性失去了显着改良。它始终是团队关注的重点,包含威逼建模、加密和深度进攻进攻。
在 Linux 上,咱们依赖 OpenSSL 进行所有加密操作,包含 TLS(HTTPS 必须)。在 macOS 和 Windows 上,咱们依赖操作系统提供的性能来实现雷同的目标。对于每个新版本的 .NET,咱们常常须要增加对新版本 OpenSSL 的反对。.NET 6 减少了对 OpenSSL 3 的反对。
OpenSSL 3 的最大变动是改良的 FIPS 140- 2 模块和更简略的许可。
.NET 6 须要 OpenSSL 1.1 或更高版本,并且会更喜爱它能够找到的最高装置版本的 OpenSSL,直到并包含 v3。在个别状况下,当您应用的 Linux 发行版默认切换到 OpenSSL 3 时,您最有可能开始应用 OpenSSL 3。大多数发行版还没有这样做。例如,如果您在 Red Hat 8 或 Ubuntu 20.04 上装置 .NET 6,您将不会(在撰写本文时)开始应用 OpenSSL 3。
OpenSSL 3、Windows 10 21H1 和 Windows Server 2022 都反对 ChaCha20Poly1305。您能够在 .NET 6 中应用这种新的通过身份验证的加密计划(假如您的环境反对它)。
感激 Kevin Jones 对 ChaCha20Poly1305 的 Linux 反对。
咱们还公布了新的运行时平安缓解路线图。重要的是,您应用的运行时不受教科书攻打类型的影响。咱们正在满足这一需要。在 .NET 6 中,咱们构建了 W^X 和英特尔控制流强制技术 (CET)的初始实现。W^X 齐全受反对,默认为 macOS Arm64 启用,并且能够抉择退出其余环境。CET 是所有环境的抉择退出和预览。咱们心愿在 .NET 7 中的所有环境中默认启用这两种技术。
Arm64
这些天来,对于笔记本电脑、云硬件和其余设施来说,Arm64 令人兴奋不已。咱们对 .NET 团队感到同样兴奋,并正在尽最大致力跟上这一行业趋势。咱们间接与 Arm Holdings、Apple 和 Microsoft 的工程师单干,以确保咱们的施行是正确和优化的,并且咱们的打算保持一致。这些亲密的合作伙伴关系对咱们帮忙很大。
- 特别感谢 Apple 在 M1 芯片公布之前向咱们的团队发送了一蒲式耳 Arm64 开发套件供咱们应用,并提供了重要的技术支持。
- 特别感谢 Arm Holdings,他们的工程师对咱们的 Arm64 更改进行了代码审查,并进行了性能改良。
在此之前,咱们通过 .NET Core 3.0 和 Arm32 增加了对 Arm64 的初始反对。该团队在最近的几个版本中都对 Arm64 进行了重大投资,并且在可预感的将来这将继续下去。在 .NET 6 中,咱们次要关注在 macOS 和 Windows Arm64 操作系统上反对新的 Apple Silicon 芯片和 x64 仿真场景。
您能够在 macOS 11+ 和 Windows 11+ Arm64 操作系统上装置 Arm64 和 x64 版本的 .NET。咱们必须做出多种设计抉择和产品更改以确保其见效。
咱们的策略是“亲原生架构”。咱们建议您始终应用与原生架构相匹配的 SDK,即 macOS 和 Windows Arm64 上的 Arm64 SDK。SDK 是大量的软件。在 Arm64 芯片上本地运行的性能将比仿真高得多。咱们更新了 CLI 以简化操作。咱们永远不会专一于优化模仿 x64。
默认状况下,如果您 dotnet run 是带有 Arm64 SDK 的 .NET 6 应用程序,它将作为 Arm64 运行。您能够应用参数轻松切换到以 x64 运行,例如 -adotnet run -a x64. 雷同的论点实用于其余 CLI 动词。无关更多信息,请参阅 实用于 macOS 和 Windows Arm64 的 .NET 6 RC2 更新。
我想确保涵盖其中的一个奥妙之处。当您应用 -a x64 时,SDK 仍以 Arm64 形式原生运行。.NET SDK 体系结构中存在过程边界的固定点。在大多数状况下,一个过程必须全是 Arm64 或全是 x64。我正在简化一点,但 .NET CLI 会期待 SDK 架构中的最初一个过程创立,而后将其作为您申请的芯片架构(如 x64)启动。这就是您的代码运行的过程。这样,作为开发人员,您能够取得 Arm64 的益处,但您的代码能够在它须要的过程中运行。这仅在您须要将某些代码作为 x64 运行时才相干。如果你不这样做,那么你能够始终以 Arm64 的形式运行所有货色,这很棒。
Arm64 反对
对于 macOS 和 Windows Arm64,以下是您须要理解的要点:
- 反对并举荐 .NET 6 Arm64 和 x64 SDK。
- 反对所有反对的 Arm64 和 x64 运行时。
- .NET Core 3.1 和 .NET 5 SDK 能够工作,但提供的性能较少,并且在某些状况下不受齐全反对。
- dotnet test 尚未与 x64 仿真一起失常工作。咱们正在致力。dotnet test 将作为 6.0.200 版本的一部分进行改良,并且可能更早。
无关更多残缺信息,请参阅.NET 对 macOS 和 Windows Arm64 的反对。
此探讨中短少 Linux。它不像 macOS 和 Windows 那样反对 x64 仿真。因而,这些新的 CLI 个性和反对办法并不间接实用于 Linux,Linux 也不须要它们。
视窗 Arm64
咱们有一个简略的工具来演示.NET 运行的环境。
C:Usersrich>dotnet tool install -g dotnet-runtimeinfo
You can invoke the tool using the following command: dotnet-runtimeinfo
Tool 'dotnet-runtimeinfo' (version '1.0.5') was successfully installed.
C:Usersrich>dotnet runtimeinfo
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
如您所见,该工具在 Windows Arm64 上本机运行。我将向您展现 ASP.NET Core 的样子。
macOS Arm64
您能够看到在 macOS Arm64 上的体验是类似的,并且还展现了架构指标。
rich@MacBook-Air app % dotnet --version
6.0.100
rich@MacBook-Air app % dotnet --info | grep RID
RID: osx-arm64
rich@MacBook-Air app % cat Program.cs
using System.Runtime.InteropServices;
using static System.Console;
WriteLine($"Hello, {RuntimeInformation.OSArchitecture} from {RuntimeInformation.FrameworkDescription}!");
rich@MacBook-Air app % dotnet run
Hello, Arm64 from .NET 6.0.0-rtm.21522.10!
rich@MacBook-Air app % dotnet run -a x64
Hello, X64 from .NET 6.0.0-rtm.21522.10!
rich@MacBook-Air app %
这张图片展现了 Arm64 执行是 Arm64 SDK 的默认设置,以及应用 - a 参数在指标 Arm64 和 x64 之间切换是如许容易。完全相同的体验实用于 Windows Arm64。
此图像演示了雷同的内容,但应用的是 ASP.NET Core。我正在应用与您在上图中看到的雷同的 .NET 6 Arm64 SDK。
Arm64 上的 Docker
Docker 反对在本机架构和仿真中运行的容器,本机架构是默认的。这看起来很显著,但当大多数 Docker Hub 目录都是面向 x64 时,这可能会让人感到困惑。您能够应用 –platform linux/amd64 来申请 x64 图像。
咱们仅反对在 Arm64 操作系统上运行 Linux Arm64 .NET 容器映像。这是因为咱们从不反对在 QEMU 中运行 .NET,这是 Docker 用于架构模仿的。看来这可能是因为 QEMU 的限度。
此图像演示了咱们保护的控制台示例:mcr.microsoft.com/dotnet/samples. 这是一个乏味的示例,因为它蕴含一些根本逻辑,用于打印您能够应用的 CPU 和内存限度信息。我展现的图像设置了 CPU 和内存限度。
本人试试吧:docker run –rm mcr.microsoft.com/dotnet/samples
Arm64 性能
Apple Silicon 和 x64 仿真反对我的项目十分重要,然而,咱们也普遍提高了 Arm64 性能。
此图像演示了将堆栈帧的内容清零的改良,这是一种常见的操作。绿线是新行为,而橙色线是另一个(不太无益的)试验,两者都绝对于基线有所改善,由蓝线示意。对于此测试,越低越好。
容器
.NET 6 更适宜容器,次要基于本文中探讨的所有改良,实用于 Arm64 和 x64。咱们还进行了有助于各种场景的要害更改。应用 .NET 6 验证容器改良演示了其中一些改良正在一起测试。
Windows 容器改良和新环境变量也蕴含在 11 月 9 日(今天)公布的 11 月 .NET Framework 4.8 容器更新中。
公布阐明可在咱们的 docker 存储库中找到:
.NET 6 容器发行阐明
.NET Framework 4.8 2021 年 11 月容器发行阐明
Windows 容器
.NET 6 减少了对 Windows 过程隔离容器的反对。如果您在 Azure Kubernetes 服务 (AKS) 中应用 Windows 容器,那么您依赖于过程隔离的容器。过程隔离容器能够被认为与 Linux 容器十分类似。Linux 容器应用 cgroups,Windows 过程隔离容器应用 Job Objects。Windows 还提供 Hyper-V 容器,通过更弱小的虚拟化提供更大的隔离。Hyper-V 容器的 .NET 6 没有任何变动。
此更改的次要价值是当初 Environment.ProcessorCount 将应用 Windows 过程隔离容器报告正确的值。如果在 64 核机器上创立 2 核容器,Environment.ProcessorCount 将返回 2. 在以前的版本中,此属性将报告机器上的处理器总数,与 Docker CLI、Kubernetes 或其余容器编排器 / 运行时指定的限度无关。此值被 .NET 的各个局部用于扩大目标,包含 .NET 垃圾收集器(只管它依赖于相干的较低级别的 API)。社区库也依赖此 API 进行扩大。
咱们最近在 AKS 上应用大量 pod 在生产中的 Windows 容器上与客户验证了这一新性能。他们可能以 50% 的内存(与他们的典型配置相比)胜利运行,这是以前导致异样的 OutOfMemoryException 程度 StackOverflowException。他们没有花工夫找到最低内存配置,但咱们猜想它显著低于他们典型内存配置的 50%。因为这一变动,他们将转向更便宜的 Azure 配置,从而节俭资金。只需降级即可,这是一个不错的、轻松的胜利。
优化缩放
咱们从用户那里据说,某些应用程序在 Environment.ProcessorCount 报告正确的值时无奈实现最佳扩大。如果这听起来与您刚刚浏览的无关 Windows 容器的内容相同,那么它有点像。.NET 6 当初提供 DOTNET_PROCESSOR_COUNT 环境变量来手动管制 Environment.ProcessorCount 的值。在典型的用例中,应用程序可能在 64 核机器上配置为 4 核,并且在 8 或 16 核方面扩大得最好。此环境变量可用于启用该缩放。
这个模型可能看起来很奇怪,其中 Environment.ProcessorCount 和 –cpus(通过 Docker CLI)值可能不同。默认状况下,容器运行时面向外围等价物,而不是理论外围。这意味着,当你说你想要 4 个外围时,你失去的 CPU 工夫与 4 个外围相当,但你的应用程序可能(实践上)在更多的外围上运行,甚至在短时间外在 64 核机器上运行所有 64 个外围。这可能使您的应用程序可能在超过 4 个线程上更好地扩大(持续示例),并且调配更多可能是无益的。这假设线程调配基于 Environment.ProcessorCount 的值。如果您抉择设置更高的值,您的应用程序可能会应用更多内存。对于某些工作负载,这是一个简略的衡量。至多,这是一个您能够测试的新选项。
Linux 和 Windows 容器均反对此新性能。
Docker 还提供了一个 CPU 组性能,您的应用程序能够关联到特定的内核。在这种状况下不倡议应用此性能,因为应用程序能够拜访的内核数量是具体定义的。咱们还看到了将它与 Hyper-V 容器一起应用时的一些问题,并且它并不是真正实用于那种隔离模式。
Debian 11“bullseye”
咱们亲密关注 Linux 发行版的生命周期和公布打算,并尝试代表您做出最佳抉择。Debian 是咱们用于默认 Linux 映像的 Linux 发行版。如果您 6.0 从咱们的一个容器存储库中提取标签,您将提取一个 Debian 映像(假如您应用的是 Linux 容器)。对于每个新的 .NET 版本,咱们都会思考是否应该采纳新的 Debian 版本。
作为一项政策,咱们不会为了不便标签而更改 Debian 版本,例如 6.0, mid-release。如果咱们这样做了,某些应用程序必定会解体。这意味着,在公布开始时抉择 Debian 版本十分重要。此外,这些图像失去了很多应用,次要是因为它们是“好标签”的援用。
Debian 和 .NET 版本天然不会一起打算。当咱们开始 .NET 6 时,咱们看到 Debian“bullseye”可能会在 2021 年公布。咱们决定从公布开始就押注于 Bullseye。咱们开始应用.NET 6 Preview 1 公布基于靶心的容器映像,并决定不再回头。赌注是 .NET 6 版本会输掉与靶心版本的竞争。到 8 月 8 日,咱们依然不晓得 Bullseye 什么时候发货,间隔咱们本人的版本公布还有三个月,即 11 月 8 日。咱们不想在预览版 Linux 上公布生产 .NET 6,但咱们保持咱们会输掉这场比赛的打算很晚。
当 Debian 11“bullseye”于 8 月 14 日公布时,咱们感到十分惊喜。咱们输掉了较量,但博得了赌注。这意味着默认状况下,.NET 6 用户从第一天开始就能够获得最佳和最新的 Debian。咱们置信 Debian 11 和 .NET 6 将是许多用户的绝佳组合。道歉,克星,咱们中了靶心。
较新的发行版在其软件包提要中蕴含各种软件包的较新次要版本,并且通常能够更快地取得 CVE 修复。这是对较新内核的补充。新发行版能够更好地为用户服务。
再往前看,咱们很快就会开始打算对 Ubuntu 22.04 的反对。Ubuntu 是另一个 Debian 系列发行版,深受 .NET 开发人员的欢送。咱们心愿为新的 Ubuntu LTS 版本提供当日反对。
向 Tianon Gravi 致敬,感激他们为社区保护 Debian 映像并在咱们有问题时帮忙咱们。
Dotnet Monitor
dotnet monitor 是容器的重要诊断工具。它作为 sidecar 容器镜像曾经有一段时间了,但处于不受反对的“试验”状态。作为 .NET 6 的一部分,咱们正在公布一个基于 .NET 6 的 dotnet monitor 映像,该映像在生产中失去齐全反对。
dotnet monitor 已被 Azure App Service 用作其 ASP.NET Core Linux 诊断体验的实现细节。这是预期的场景之一,建设在 dotnet monitor 之上,以提供更高级别和更高价值的体验。
您当初能够拉取新图像:
docker pull mcr.microsoft.com/dotnet/monitor:6.0
dotnet monitor 使从 .NET 过程拜访诊断信息(日志、跟踪、过程转储)变得更加容易。在台式机上拜访所需的所有诊断信息很容易,然而,这些相熟的技术在应用容器的生产环境中可能不起作用。dotnet monitor 提供了一种对立的形式来收集这些诊断工件,无论是在您的桌面计算机上还是在 Kubernetes 集群中运行。收集这些诊断工件有两种不同的机制:
- 用于长期收集工件的 HTTP API。当您曾经晓得您的应用程序遇到问题并且您有趣味收集更多信息时,您能够调用这些 API 端点。
- 基于规定的配置触发器,用于始终在线收集工件。您能够配置规定以在满足所需条件时收集诊断数据,例如,当您继续高 CPU 时收集过程转储。
dotnet monitor 为 .NET 应用程序提供了一个通用的诊断 API,能够应用任何工具在任何中央工作。“通用 API”不是 .NET API,而是您能够调用和查问的 Web API。dotnet monitor 包含一个 ASP.NET Web 服务器,它间接与 .NET 运行时中的诊断服务器交互并公开来自诊断服务器的数据。的设计 dotnet monitor 可实现生产中的高性能监控和平安应用,以管制对特权信息的拜访。dotnet monitor 通过非 Internet 可寻址的 unix domain socket 与运行时交互——逾越容器边界。该模型通信模型非常适合此用例。
结构化 JSON 日志
JSON 格式化程序当初是 aspnet.NET 6 容器映像中的默认控制台记录器。.NET 5 中的默认设置为简略的控制台格式化程序。进行此更改是为了使默认配置与依赖机器可读格局(如 JSON)的自动化工具一起应用。
图像的输入当初如下所示
aspnet:$ docker run --rm -it -p 8000:80 mcr.microsoft.com/dotnet/samples:aspnetapp
{"EventId":60,"LogLevel":"Warning","Category":"Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository","Message":"Storing keys in a directory u0027/root/.aspnet/DataProtection-Keysu0027 that may not be persisted outside of the container. Protected data will be unavailable when container is destroyed.","State":{"Message":"Storing keys in a directory u0027/root/.aspnet/DataProtection-Keysu0027 that may not be persisted outside of the container. Protected data will be unavailable when container is destroyed.","path":"/root/.aspnet/DataProtection-Keys","{OriginalFormat}":"Storing keys in a directory u0027{path}u0027 that may not be persisted outside of the container. Protected data will be unavailable when container is destroyed."}}
{"EventId":35,"LogLevel":"Warning","Category":"Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager","Message":"No XML encryptor configured. Key {86cafacf-ab57-434a-b09c-66a929ae4fd7} may be persisted to storage in unencrypted form.","State":{"Message":"No XML encryptor configured. Key {86cafacf-ab57-434a-b09c-66a929ae4fd7} may be persisted to storage in unencrypted form.","KeyId":"86cafacf-ab57-434a-b09c-66a929ae4fd7","{OriginalFormat}":"No XML encryptor configured. Key {KeyId:B} may be persisted to storage in unencrypted form."}}
{"EventId":14,"LogLevel":"Information","Category":"Microsoft.Hosting.Lifetime","Message":"Now listening on: http://[::]:80","State":{"Message":"Now listening on: http://[::]:80","address":"http://[::]:80","{OriginalFormat}":"Now listening on: {address}"}}
{"EventId":0,"LogLevel":"Information","Category":"Microsoft.Hosting.Lifetime","Message":"Application started. Press Ctrlu002BC to shut down.","State":{"Message":"Application started. Press Ctrlu002BC to shut down.","{OriginalFormat}":"Application started. Press Ctrlu002BC to shut down."}}
{"EventId":0,"LogLevel":"Information","Category":"Microsoft.Hosting.Lifetime","Message":"Hosting environment: Production","State":{"Message":"Hosting environment: Production","envName":"Production","{OriginalFormat}":"Hosting environment: {envName}"}}
{"EventId":0,"LogLevel":"Information","Category":"Microsoft.Hosting.Lifetime","Message":"Content root path: /app","State":{"Message":"Content root path: /app","contentRoot":"/app","{OriginalFormat}":"Content root path: {contentRoot}"}}
Logging__Console__FormatterName 能够通过设置或勾销设置环境变量或通过代码更改来更改记录器格局类型(无关更多详细信息,请参阅控制台日志格局)。
更改后,您将看到如下输入(就像 .NET 5 一样):
$ docker run --rm -it -p 8000:80 -e Logging__Console__FormatterName="" mcr.microsoft.com/dotnet/samples:aspnetapp
warn: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[60]
Storing keys in a directory '/root/.aspnet/DataProtection-Keys' that may not be persisted outside of the container. Protected data will be unavailable when container is destroyed.
warn: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[35]
No XML encryptor configured. Key {8d4ddd1d-ccfc-4898-9fe1-3e7403bf23a0} may be persisted to storage in unencrypted form.
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://[::]:80
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
Content root path: /app
留神:此更改不会影响开发人员计算机上的 .NET SDK,例如 dotnet run. 此更改特定于 aspnet 容器映像。
反对 OpenTelemetry 指标
作为咱们关注可察看性的一部分,咱们始终在为最初几个 .NET 版本增加对 OpenTelemetry 的反对。在 .NET 6 中,咱们增加了对 OpenTelemetry Metrics API 的反对。通过增加对 OpenTelemetry 的反对,您的应用程序能够与其余 OpenTelemetry 零碎无缝互操作。
System.Diagnostics.Metrics 是 OpenTelemetry Metrics API 标准的 .NET 实现。Metrics API 是专门为解决原始测量而设计的,目标是高效、同时地生成这些测量的间断摘要。
API 包含 Meter 可用于创立仪器对象的类。API 公开了四个工具类:Counter、Histogram、ObservableCounter 和,ObservableGauge 以反对不同的度量计划。此外,API 公开 MeterListener 该类以容许收听仪器记录的测量值,以用于聚合和分组目标。
OpenTelemetry .NET 实现将被扩大以应用这些新的 API,这些 API 增加了对 Metrics 可察看性场景的反对。
图书馆测量记录示例
Meter meter = new Meter("io.opentelemetry.contrib.mongodb", "v1.0");
Counter<int> counter = meter.CreateCounter<int>("Requests");
counter.Add(1);
counter.Add(1, KeyValuePair.Create<string, object>("request", "read"));
听力示例
MeterListener listener = new MeterListener();
listener.InstrumentPublished = (instrument, meterListener) =>
{if (instrument.Name == "Requests" && instrument.Meter.Name == "io.opentelemetry.contrib.mongodb")
{meterListener.EnableMeasurementEvents(instrument, null);
}
};
listener.SetMeasurementEventCallback<int>((instrument, measurement, tags, state) =>
{Console.WriteLine($"Instrument: {instrument.Name} has recorded the measurement {measurement}");
});
listener.Start();
Windows Forms
咱们持续在 Windows 窗体中进行重要改良。.NET 6 包含更好的控件可拜访性、设置应用程序范畴的默认字体、模板更新等的能力。
可拜访性改良
在此版本中,咱们增加了用于 CheckedListBox、LinkLabel、Panel、ScrollBar 和 TabControlTrackBar 的 UIA 提供程序,它们使讲述人等工具和测试自动化可能与应用程序的元素进行交互。
默认字体
您当初能够应用.Application.SetDefaultFont
void Application.SetDefaultFont(Font font)
最小的应用程序
以下是带有 .NET 6 的最小 Windows 窗体应用程序:
class Program
{[STAThread]
static void Main()
{ApplicationConfiguration.Initialize();
Application.Run(new Form1());
}
}
作为 .NET 6 版本的一部分,咱们始终在更新大多数模板,使其更加古代和简洁,包含 Windows 窗体。咱们决定让 Windows 窗体模板更传统一些,局部起因是须要将 [STAThread] 属性利用于应用程序入口点。然而,还有更多的戏剧而不是立刻呈现在眼前。
ApplicationConfiguration.Initialize()是一个源生成 API,它在后盾收回以下调用:
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.SetDefaultFont(new Font(…));
Application.SetHighDpiMode(HighDpiMode.SystemAware);
这些调用的参数可通过 csproj 或 props 文件中的 MSBuild 属性进行配置。
Visual Studio 2022 中的 Windows 窗体设计器也晓得这些属性(目前它只读取默认字体),并且能够向您显示您的应用程序,就像它在运行时一样:
模板更新
C# 的 Windows 窗体模板已更新,以反对新的应用程序疏导、global using 指令、文件范畴的命名空间和可为空的援用类型。
更多运行时 designers
当初您能够构建通用设计器(例如,报表设计器),因为 .NET 6 具备设计器和与设计器相干的基础架构所短少的所有局部。无关详细信息,请参阅此博客文章。
单文件利用
在 .NET 6 中,已为 Windows 和 macOS 启用内存中单文件应用程序。在 .NET 5 中,这种部署类型仅限于 Linux。您当初能够为所有受反对的操作系统公布作为单个文件部署和启动的单文件二进制文件。单文件利用不再将任何外围运行时程序集提取到长期目录。
这种扩大性能基于称为“超级主机”的构建块。“apphost”是在非单文件状况下启动应用程序的可执行文件,例如 myapp.exe 或./myapp. Apphost 蕴含用于查找运行时、加载它并应用该运行时启动您的应用程序的代码。Superhost 依然执行其中一些工作,但应用所有 CoreCLR 本机二进制文件的动态链接正本。动态链接是咱们用来实现繁多文件体验的办法。本机依赖项(如 NuGet 包附带的)是单文件嵌入的显着例外。默认状况下,它们不蕴含在单个文件中。例如,WPF 本机依赖项不是超级主机的一部分,因而会在单文件应用程序之外产生其余文件。您能够应用该设置 IncludeNativeLibrariesForSelfExtract 嵌入和提取本机依赖项。
动态剖析
咱们改良了单文件分析器以容许自定义正告。如果您的 API 在单文件公布中不起作用,您当初能够应用 [RequiresAssemblyFiles] 属性对其进行标记,如果启用了分析器,则会呈现正告。增加该属性还将使办法中与单个文件相干的所有正告静音,因而您能够应用该正告将正告向上流传到您的公共 API。
当 PublishSingleFile 设置为 true 时,会主动为 exe 我的项目启用单文件分析器,但您也能够通过将 EnableSingleFileAnalysis 设置为 true 来为任何我的项目启用它。如果您想反对将库作为单个文件应用程序的一部分,这将很有帮忙。
在 .NET 5 中,咱们为单文件包中行为不同的 Assembly.Location 和一些其余 API 增加了正告。
压缩
单文件包当初反对压缩,能够通过将属性设置 EnableCompressionInSingleFile 为 true. 在运行时,文件会依据须要解压缩到内存中。压缩能够为某些场景节俭大量空间。
让咱们看一下与 NuGet 包资源管理器一起应用的单个文件公布(带压缩和不带压缩)。
无压缩:172 MB
压缩:71.6 MB
压缩会显着减少应用程序的启动工夫,尤其是在 Unix 平台上。Unix 平台有一个不能用于压缩的无拷贝疾速启动门路。您应该在启用压缩后测试您的应用程序,看看额定的启动老本是否能够承受。
单文件调试
目前只能应用平台调试器(如 WinDBG)来调试单文件应用程序。咱们正在思考应用更高版本的 Visual Studio 2022 增加 Visual Studio 调试。
macOS 上的单文件签名
单文件应用程序当初满足 macOS 上的 Apple 公证和签名要求。具体更改与咱们依据离散文件布局构建单文件应用程序的形式无关。
Apple 开始对 macOS Catalina 施行新的签名和公证要求。咱们始终在与 Apple 密切合作,以理解需要,并寻找使 .NET 等开发平台可能在该环境中失常工作的解决方案。咱们曾经进行了产品更改并记录了用户工作流程,以满足 Apple 在最近几个 .NET 版本中的要求。剩下的差距之一是单文件签名,这是在 macOS 上散发 .NET 应用程序的要求,包含在 macOS 商店中。
IL 修整
该团队始终致力于为多个版本进行 IL 修整。.NET 6 代表了这一旅程向前迈出的重要一步。咱们始终在致力使更激进的修剪模式平安且可预测,因而有信念将其设为默认模式。TrimMode=link 以前是可选性能,当初是默认性能。
咱们有一个三管齐下的修剪策略:
- 进步平台的修剪能力。
- 对平台进行正文以提供更好的正告并使其他人也能这样做。
- 在此基础上,让默认的修剪模式更具侵略性,以便让应用程序变小。
因为应用未正文反射的应用程序的后果不牢靠,修剪之前始终处于预览状态。有了修剪正告,体验当初应该是可预测的。没有修剪正告的应用程序应该正确修剪并且在运行时察看到行为没有变动。目前,只有外围的 .NET 库曾经齐全注解了修剪,但咱们心愿看到生态系统正文修剪并兼容修剪
减小应用程序大小
让咱们应用 SDK 工具之一的 crossgen 来看看这个修剪改良。它能够通过几个修剪正告进行修剪,crossgen 团队可能解决。
首先,让咱们看一下将 crossgen 公布为一个独立的应用程序而无需修剪。它是 80 MB(包含 .NET 运行时和所有库)。
而后咱们能够尝试(当初是旧版).NET 5 默认修剪模式,copyused. 后果降至 55 MB。
新的 .NET 6 默认修剪模式 link 将独立文件大小进一步升高到 36MB。
咱们心愿新的 link 修剪模式能更好地与修剪的冀望保持一致:显着节俭和可预测的后果。
默认启用正告
修剪正告告诉您修剪可能会删除运行时应用的代码的中央。这些正告以前默认禁用,因为正告十分嘈杂,次要是因为 .NET 平台没有参加修剪作为第一类场景。
咱们对大部分 .NET 库进行了正文,以便它们产生精确的修剪正告。因而,咱们感觉是时候默认启用修剪正告了。ASP.NET Core 和 Windows 桌面运行时库尚未正文。咱们打算接下来正文 ASP.NET 服务组件(在 .NET 6 之后)。咱们心愿看到社区在 .NET 6 公布后对 NuGet 库进行正文。
您能够通过设置 <SuppressTrimAnalysisWarnings> 为 true 来禁用正告。
更多信息:
- 修剪正告
- 修剪介绍
- 筹备 .NET 库以进行修剪
与本机 AOT 共享
咱们也为 Native AOT 试验实现了雷同的修剪正告,这应该会以简直雷同的形式改善 Native AOT 编译体验。
数学
咱们显着改良了数学 API。社区中的一些人曾经在享受这些改良。
面向性能的 API
System.Math 中增加了面向性能的数学 API。如果底层硬件反对,它们的实现是硬件加速的。
新 API:
- SinCos 用于同时计算 Sin 和 Cos。
- ReciprocalEstimate 用于计算 1 / x 的近似值。
- ReciprocalSqrtEstimate 用于计算 1 / Sqrt(x) 的近似值。
新的重载:
- Clamp, DivRem,Min 和 Max 反对 nint 和 nuint。
- Abs 和 Sign 反对 nint。
- DivRem 变体返回 tuple。
性能改良:
ScaleB 被移植到 C# 导致调用速度进步了 93%。感激亚历克斯·科文顿。
大整数性能
改良了从十进制和十六进制字符串中解析 BigIntegers。咱们看到了高达 89% 的改良,如下图所示(越低越好)。
感激约瑟夫·达席尔瓦。
ComplexAPI 当初正文为 readonly
当初对各种 API 进行了正文,System.Numerics.Complexreadonly 以确保不会对 readonly 值或传递的值进行复制 in。
归功于 hrrrrustic。
BitConverter 当初反对浮点到无符号整数位播送
BitConverter 当初反对 DoubleToUInt64Bits, HalfToUInt16Bits, SingleToUInt32Bits, UInt16BitsToHalf, UInt32BitsToSingle, 和 UInt64BitsToDouble. 这应该使得在须要时更容易进行浮点位操作。
归功于 Michal Petryka。
BitOperations 反对附加性能
BitOperations 当初反对 IsPow2,RoundUpToPowerOf2 和提供 nint/nuint 重载现有函数。
感激约翰凯利、霍耀源和罗宾林德纳。
Vector<T>, Vector2, Vector3, 和 Vector4 改良
Vector<T> 当初反对 C# 9 中增加的原始类型 nint 和 nuint 原始类型。例如,此更改应该能够更简略地应用带有指针或平台相干长度类型的 SIMD 指令。
Vector<T> 当初反对一种 Sum 办法来简化计算向量中所有元素的“程度和”的须要。归功于伊万兹拉塔诺夫。
Vector<T> 当初反对一种通用办法 As<TFrom, TTo> 来简化在具体类型未知的通用上下文中解决向量。感激霍耀源
重载反对 Span<T> 已增加到 Vector2、Vector3 和 Vector4 以改善须要加载或存储矢量类型时的体验。
更好地解析规范数字格局
咱们改良了规范数字类型的解析器,特地是.ToString 和.TryFormatParse。他们当初将了解对精度 > 99 位小数的要求,并将为那么多位数提供精确的后果。此外,解析器当初更好地反对办法中的尾随零。
以下示例演示了之前和之后的行为。
-
32.ToString(“C100”)->C132
- .NET 6:$32.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
- .NET 5:咱们在格式化代码中人为限度只能解决 <= 99 的精度。对于精度 >= 100,咱们改为将输出解释为自定义格局。
-
32.ToString(“H99”)-> 扔一个 FormatException
- .NET 6:抛出 FormatException
- 这是正确的行为,但在这里调用它是为了与下一个示例进行比照。
-
32.ToString(“H100”)->H132
- .NET 6:抛出 FormatException
- .NET 5:H 是有效的格局说明符。所以,咱们应该抛出一个 FormatException. 相同,咱们将精度 >= 100 解释为自定义格局的错误行为意味着咱们返回了谬误的值。
-
double.Parse(“9007199254740997.0”)->9007199254740998
- .NET 6 9007199254740996:。
- .NET 5:9007199254740997.0 不能齐全以 IEEE 754 格局示意。应用咱们以后的舍入计划,正确的返回值应该是 9007199254740996. 然而,输出的最初一部分迫使解析器谬误地舍入后果并返回。.09007199254740998
System.Text.Json
System.Text.Json 提供多种高性能 API 用于解决 JSON 文档。在过来的几个版本中,咱们增加了新性能,以进一步提高 JSON 解决性能并加重对心愿从 NewtonSoft.Json 迁徙的人的妨碍。此版本包含在该门路上的持续,并且在性能方面向前迈出了一大步,特地是在序列化程序源生成器方面。
JsonSerializer 源生成
留神:应用 .NET 6 RC1 或更早版本的源代码生成的应用程序应从新编译。
简直所有 .NET 序列化程序的支柱都是反射。反射对于某些场景来说是一种很好的能力,但不能作为高性能云原生应用程序(通常(反)序列化和解决大量 JSON 文档)的根底。反射是启动、内存应用和程序集修整的问题。
运行时反射的代替办法是编译时源代码生成。在 .NET 6 中,咱们蕴含一个新的源代码生成器作为 System.Text.Json. JSON 源代码生成器能够与多种形式联合应用 JsonSerializer 并且能够通过多种形式进行配置。
它能够提供以下益处:
- 缩小启动工夫
- 进步序列化吞吐量
- 缩小公有内存应用
- 删除运行时应用 System.Reflection 和 System.Reflection.Emit
- IL 修整兼容性
默认状况下,JSON 源生成器为给定的可序列化类型收回序列化逻辑。JsonSerializer 通过生成间接应用的源代码,这提供了比应用现有办法更高的性能 Utf8JsonWriter。简而言之,源代码生成器提供了一种在编译时为您提供不同实现的办法,以使运行时体验更好。
给定一个简略的类型:
namespace Test
{
internal class JsonMessage
{public string Message { get; set;}
}
}
源生成器能够配置为为示例 JsonMessage 类型的实例生成序列化逻辑。请留神,类名 JsonContext 是任意的。您能够为生成的源应用所需的任何类名。
using System.Text.Json.Serialization;
namespace Test
{[JsonSerializable(typeof(JsonMessage)]
internal partial class JsonContext : JsonSerializerContext
{}}
应用此模式的序列化程序调用可能相似于以下示例。此示例提供了可能的最佳性能。
using MemoryStream ms = new();
using Utf8JsonWriter writer = new(ms);
JsonSerializer.Serialize(jsonMessage, JsonContext.Default.JsonMessage);
writer.Flush();
// Writer contains:
// {"Message":"Hello, world!"}
最快和最优化的源代码生成模式——基于 Utf8JsonWriter——目前仅可用于序列化。Utf8JsonReader 依据您的反馈,未来可能会提供对反序列化的相似反对。
源生成器还收回类型元数据初始化逻辑,这也有利于反序列化。JsonMessage 要反序列化应用预生成类型元数据的实例,您能够执行以下操作:
JsonSerializer.Deserialize(json, JsonContext.Default.JsonMessage);
JsonSerializer 反对 IAsyncEnumerable
您当初能够应用 System.Text.Json(反)序列化 IAsyncEnumerable<T>JSON 数组。以下示例应用流作为任何异步数据源的示意。源能够是本地计算机上的文件,也能够是数据库查问或 Web 服务 API 调用的后果。
JsonSerializer.SerializeAsync 已更新以辨认并为 IAsyncEnumerable 值提供非凡解决。
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
static async IAsyncEnumerable<int> PrintNumbers(int n)
{for (int i = 0; i < n; i++) yield return i;
}
using Stream stream = Console.OpenStandardOutput();
var data = new {Data = PrintNumbers(3) };
await JsonSerializer.SerializeAsync(stream, data); // prints {"Data":[0,1,2]}
IAsyncEnumerable 仅应用异步序列化办法反对值。尝试应用同步办法进行序列化将导致 NotSupportedException 被抛出。
流式反序列化须要一个新的 API 来返回 IAsyncEnumerable<T>. 咱们为此增加了 JsonSerializer.DeserializeAsyncEnumerable 办法,您能够在以下示例中看到。
using System;
using System.IO;
using System.Text;
using System.Text.Json;
var stream = new MemoryStream(Encoding.UTF8.GetBytes("[0,1,2,3,4]"));
await foreach (int item in JsonSerializer.DeserializeAsyncEnumerable<int>(stream))
{Console.WriteLine(item);
}
此示例将按需反序列化元素,并且在应用特地大的数据流时十分有用。它仅反对从根级 JSON 数组读取,只管未来可能会依据反馈放宽。
现有 DeserializeAsync 办法名义上反对 IAsyncEnumerable<T>,但在其非流办法签名的范畴内。它必须将最终后果作为单个值返回,如以下示例所示。
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.Json;
var stream = new MemoryStream(Encoding.UTF8.GetBytes(@"{""Data"":[0,1,2,3,4]}"));
var result = await JsonSerializer.DeserializeAsync<MyPoco>(stream);
await foreach (int item in result.Data)
{Console.WriteLine(item);
}
public class MyPoco
{public IAsyncEnumerable<int> Data { get; set;}
}
在此示例中,反序列化器将 IAsyncEnumerable 在返回反序列化对象之前缓冲内存中的所有内容。这是因为反序列化器须要在返回后果之前耗费整个 JSON 值。
System.Text.Json:可写 DOM 性能
可写 JSON DOM 个性为 System.Text.Json 增加了一个新的简略且高性能的编程模型。这个新的 API 很有吸引力,因为它防止了须要强类型的序列化合约,并且与现有的 JsonDocument 类型相比,DOM 是可变的。
这个新的 API 有以下益处:
在应用 POCO 类型是不可能或不心愿的状况下,或者当 JSON 模式不固定且必须查看的状况下,序列化的轻量级代替计划。
启用对大树子集的无效批改。例如,能够无效地导航到大型 JSON 树的子局部并从该子局部读取数组或反序列化 POCO。LINQ 也能够与它一起应用。
以下示例演示了新的编程模型。
// Parse a JSON object
JsonNode jNode = JsonNode.Parse("{"MyProperty":42}");
int value = (int)jNode["MyProperty"];
Debug.Assert(value == 42);
// or
value = jNode["MyProperty"].GetValue<int>();
Debug.Assert(value == 42);
// Parse a JSON array
jNode = JsonNode.Parse("[10,11,12]");
value = (int)jNode[1];
Debug.Assert(value == 11);
// or
value = jNode[1].GetValue<int>();
Debug.Assert(value == 11);
// Create a new JsonObject using object initializers and array params
var jObject = new JsonObject
{["MyChildObject"] = new JsonObject
{["MyProperty"] = "Hello",
["MyArray"] = new JsonArray(10, 11, 12)
}
};
// Obtain the JSON from the new JsonObject
string json = jObject.ToJsonString();
Console.WriteLine(json); // {"MyChildObject":{"MyProperty":"Hello","MyArray":[10,11,12]}}
// Indexers for property names and array elements are supported and can be chained
Debug.Assert(jObject["MyChildObject"]["MyArray"][1].GetValue<int>() == 11);
ReferenceHandler.IgnoreCycles
JsonSerializer(System.Text.Json)当初反对在序列化对象图时疏忽循环的能力。该 ReferenceHandler.IgnoreCycles 选项具备与 Newtonsoft.Json ReferenceLoopHandling.Ignore 相似的行为。一个要害区别是 System.Text.Json 实现用 null JSON 标记替换援用循环,而不是疏忽对象援用。
您能够在以下示例中看到 ReferenceHandler.IgnoreCycles 的行为。在这种状况下,该 Next 属性被序列化为 null,因为否则它会创立一个循环。
class Node
{public string Description { get; set;}
public object Next {get; set;}
}
void Test()
{var node = new Node { Description = "Node 1"};
node.Next = node;
var opts = new JsonSerializerOptions {ReferenceHandler = ReferenceHandler.IgnoreCycles};
string json = JsonSerializer.Serialize(node, opts);
Console.WriteLine(json); // Prints {"Description":"Node 1","Next":null}
}
源代码构建
通过源代码构建,您只需几个命令即可在您本人的计算机上从源代码构建 .NET SDK。让我解释一下为什么这个我的项目很重要。
源代码构建是一个场景,也是咱们在公布 .NET Core 1.0 之前始终与 Red Hat 合作开发的基础架构。几年后,咱们十分靠近于交付它的全自动版本。对于 Red Hat Enterprise Linux (RHEL) .NET 用户来说,这个性能很重要。Red Hat 通知咱们,.NET 曾经倒退成为其生态系统的重要开发者平台。好的!
Linux 发行版的黄金规范是应用作为发行版存档一部分的编译器和工具链构建开源代码。这实用于 .NET 运行时(用 C++ 编写),但不适用于任何用 C# 编写的代码。对于 C# 代码,咱们应用两遍构建机制来满足发行版要求。这有点简单,但理解流程很重要。
Red Hat 应用 .NET SDK (#1) 的 Microsoft 二进制构建来构建 .NET SDK 源代码,以生成 SDK (#2) 的纯开源二进制构建。之后,应用这个新版本的 SDK (#2) 再次构建雷同的 SDK 源代码,以生成可证实的开源 SDK (#3)。.NET SDK (#3) 的最终二进制版本随后可供 RHEL 用户应用。之后,Red Hat 能够应用雷同的 SDK (#3) 来构建新的 .NET 版本,而不再须要应用 Microsoft SDK 来构建每月更新。
这个过程可能令人诧异和困惑。开源发行版须要通过开源工具构建。此模式确保不须要 Microsoft 构建的 SDK,无论是无意还是无心。作为开发者平台,蕴含在发行版中的门槛比仅应用兼容许可证的门槛更高。源代码构建我的项目使 .NET 可能满足该规范。
源代码构建的可交付成绩是源代码压缩包。源 tarball 蕴含 SDK 的所有源(对于给定版本)。从那里,红帽(或其余组织)能够构建本人的 SDK 版本。Red Hat 政策要求应用内置源工具链来生成二进制 tar 球,这就是他们应用两遍办法的起因。然而源代码构建自身不须要这种两遍办法。
在 Linux 生态系统中,给定组件同时领有源和二进制包或 tarball 是很常见的。咱们曾经有了可用的二进制 tarball,当初也有了源 tarball。这使得 .NET 与规范组件模式相匹配。
.NET 6 的重大改良是源 tarball 当初是咱们构建的产品。它过来须要大量的人工来制作,这也导致将源 tarball 交付给 Red Hat 的提早很长。单方都对此不称心。
在这个我的项目上,咱们与红帽密切合作五年多。它的胜利在很大水平上要归功于咱们有幸与之共事的优良红帽工程师的致力。其余发行版和组织曾经并将从他们的致力中受害。
附带阐明一下,源代码构建是朝着可重现构建迈出的一大步,咱们也深信这一点。.NET SDK 和 C# 编译器具备重要的可重现构建性能。
库 API
除了曾经涵盖的 API 之外,还增加了以下 API。
WebSocket 压缩
压缩对于通过网络传输的任何数据都很重要。WebSockets 当初启用压缩。咱们应用了 WebSockets 的扩大 permessage-deflate 实现,RFC 7692。它容许应用该 DEFLATE 算法压缩 WebSockets 音讯负载。此性能是 GitHub 上 Networking 的次要用户申请之一。
与加密一起应用的压缩可能会导致攻打,例如 CRIME 和 BREACH。这意味着不能在单个压缩上下文中将机密与用户生成的数据一起发送,否则能够提取该机密。为了让用户留神到这些影响并帮忙他们衡量危险,咱们将其中一个要害 API 命名为 DangerousDeflateOptions。咱们还增加了敞开特定消息压缩的性能,因而如果用户想要发送机密,他们能够在不压缩的状况下平安地执行此操作。
禁用压缩时 WebSocket 的内存占用缩小了约 27%。
从客户端启用压缩很容易,如下例所示。然而,请记住,服务器能够协商设置,例如申请更小的窗口或齐全回绝压缩。
var cws = new ClientWebSocket();
cws.Options.DangerousDeflateOptions = new WebSocketDeflateOptions()
{
ClientMaxWindowBits = 10,
ServerMaxWindowBits = 10
};
还增加了对 ASP.NET Core 的 WebSocket 压缩反对。
归功于伊万兹拉塔诺夫。
Socks 代理反对
SOCKS 是一种代理服务器实现,能够解决任何 TCP 或 UDP 流量,使其成为一个十分通用的零碎。这是一个长期存在的社区申请,已增加到 .NET 6 中。
此更改减少了对 Socks4、Socks4a 和 Socks5 的反对。例如,它能够通过 SSH 测试内部连贯或连贯到 Tor 网络。
该类 WebProxy 当初承受 socks 计划,如以下示例所示。
var handler = new HttpClientHandler
{Proxy = new WebProxy("socks5://127.0.0.1", 9050)
};
var httpClient = new HttpClient(handler);
归功于 Huo yaoyuan。
Microsoft.Extensions.Hosting — 配置主机选项 API
咱们在 IHostBuilder 上增加了一个新的 ConfigureHostOptions API,以简化应用程序设置(例如,配置敞开超时):
using HostBuilder host = new()
.ConfigureHostOptions(o =>
{o.ShutdownTimeout = TimeSpan.FromMinutes(10);
})
.Build();
host.Run();
在 .NET 5 中,配置主机选项有点简单:
using HostBuilder host = new()
.ConfigureServices(services =>
{
services.Configure<HostOptions>(o =>
{o.ShutdownTimeout = TimeSpan.FromMinutes(10);
});
})
.Build();
host.Run();
Microsoft.Extensions.DependencyInjection — CreateAsyncScope API
CreateAsyncScope 创立 API 是为了解决服务的处理 IAsyncDisposable。以前,您可能曾经留神到处理 IAsyncDisposable 服务提供者可能会引发 InvalidOperationException 异样。
以下示例演示了新模式,CreateAsyncScope 用于启用 using 语句的平安应用。
await using (var scope = provider.CreateAsyncScope())
{var foo = scope.ServiceProvider.GetRequiredService<Foo>();
}
以下示例演示了现有的问题案例:
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
await using var provider = new ServiceCollection()
.AddScoped<Foo>()
.BuildServiceProvider();
// This using can throw InvalidOperationException
using (var scope = provider.CreateScope())
{var foo = scope.ServiceProvider.GetRequiredService<Foo>();
}
class Foo : IAsyncDisposable
{public ValueTask DisposeAsync() => default;
}
以下模式是先前倡议的防止异样的解决办法。不再须要它。
var scope = provider.CreateScope();
var foo = scope.ServiceProvider.GetRequiredService<Foo>();
await ((IAsyncDisposable)scope).DisposeAsync();
感激 Martin Björkström。
Microsoft.Extensions.Logging — 编译时源生成器
.NET 6 引入了 LoggerMessageAttribute 类型. 此属性是 Microsoft.Extensions.Logging 命名空间的一部分,应用时,它会源生成高性能日志记录 API。源生成日志反对旨在为古代 .NET 应用程序提供高度可用和高性能的日志解决方案。主动生成的源代码依赖于 ILogger 接口和 LoggerMessage.Define 性能。
LoggerMessageAttribute 源生成器在用于 partial 日志记录办法时触发。当被触发时,它要么可能主动生成 partial 它正在装璜的办法的实现,要么生成编译时诊断,并提供无关正确应用的提醒。编译时日志记录解决方案在运行时通常比现有的日志记录办法快得多。它通过最大限度地打消装箱、长期调配和副原本实现这一点。
与间接手动应用 LoggerMessage.Define API 相比,有以下益处:
- 更短更简略的语法:申明性属性应用而不是编码样板。
- 疏导式开发人员体验:生成器收回正告以帮忙开发人员做正确的事件。
- 反对任意数量的日志记录参数。LoggerMessage.Define 最多反对六个。
- 反对动静日志级别。这是 LoggerMessage.Define 独自不可能的。
要应用 LoggerMessageAttribute,消费类和办法须要是 partial。代码生成器在编译时触发并生成 partial 办法的实现。
public static partial class Log
{[LoggerMessage(EventId = 0, Level = LogLevel.Critical, Message = "Could not open socket to `{hostName}`")]
public static partial void CouldNotOpenSocket(ILogger logger, string hostName);
}
在后面的示例中,日志记录办法是 static,并且在属性定义中指定了日志级别。在动态上下文中应用属性时,ILogger 须要实例作为参数。您也能够抉择在非动态上下文中应用该属性。无关更多示例和应用场景,请拜访编译时日志记录源生成器文档。
System.Linq — 可枚举的反对 Index 和 Range 参数
该 Enumerable.ElementAt 办法当初承受来自可枚举开端的索引,如以下示例所示。
Enumerable.Range(1, 10).ElementAt(^2); // returns 9
增加了一个 Enumerable.Take 承受 Range 参数的重载。它简化了对可枚举序列的切片:
- source.Take(..3)代替 source.Take(3)
- source.Take(3..)代替 source.Skip(3)
- source.Take(2..7)代替 source.Take(7).Skip(2)
- source.Take(^3..)代替 source.TakeLast(3)
- source.Take(..^3)代替 source.SkipLast(3)
- source.Take(^7..^3)而不是.source.TakeLast(7).SkipLast(3)
感激 @dixin。
System.Linq —TryGetNonEnumeratedCount
该 TryGetNonEnumeratedCount 办法尝试在不强制枚举的状况下获取源可枚举的计数。这种办法在枚举之前预调配缓冲区很有用的场景中很有用,如上面的示例所示。
List<T> buffer = source.TryGetNonEnumeratedCount(out int count) ? new List<T>(capacity: count) : new List<T>();
foreach (T item in source)
{
buffer.Add(item);
}
TryGetNonEnumeratedCount 查看实现 ICollection/ ICollection<T> 或利用 Linq 采纳的一些外部优化的源。
System.Linq — DistinctBy/ UnionBy/ IntersectBy/ExceptBy
新变体已增加到容许应用键选择器函数指定相等性的汇合操作中,如下例所示。
Enumerable.Range(1, 20).DistinctBy(x => x % 3); // {1, 2, 3}
var first = new (string Name, int Age)[] { ("Francis", 20), ("Lindsey", 30), ("Ashley", 40) };
var second = new (string Name, int Age)[] { ("Claire", 30), ("Pat", 30), ("Drew", 33) };
first.UnionBy(second, person => person.Age); // {("Francis", 20), ("Lindsey", 30), ("Ashley", 40), ("Drew", 33) }
System.Linq – MaxBy/MinBy
MaxBy 和 MinBy 办法容许应用键选择器查找最大或最小元素,如下例所示。
var people = new (string Name, int Age)[] { (“Francis”, 20), (“Lindsey”, 30), (“Ashley”, 40) };
people.MaxBy(person => person.Age); // (“Ashley”, 40)
System.Linq —Chunk
Chunk 可用于将可枚举的源分块为固定大小的切片,如下例所示。
IEnumerable<int[]> chunks = Enumerable.Range(0, 10).Chunk(size: 3); // {{0,1,2}, {3,4,5}, {6,7,8}, {9} }
归功于罗伯特安德森。
System.Linq—— //FirstOrDefault 采纳默认参数的重载 LastOrDefaultSingleOrDefault
如果源可枚举为空,则现有的 FirstOrDefault /LastOrDefault /SingleOrDefault 办法返回 default(T)。增加了新的重载,它们承受在这种状况下返回的默认参数,如以下示例所示。
Enumerable.Empty<int>().SingleOrDefault(-1); // returns -1
感激 @ Foxtrek64。
System.Linq —Zip 承受三个可枚举的重载
Zip 办法当初反对组合三个枚举,如以下示例所示。
var xs = Enumerable.Range(1, 10);
var ys = xs.Select(x => x.ToString());
var zs = xs.Select(x => x % 2 == 0);
foreach ((int x, string y, bool z) in Enumerable.Zip(xs,ys,zs))
{}
归功于 Huo yaoyuan。
优先队列
PriorityQueue<TElement, TPriority>(System.Collections.Generic) 是一个新汇合,能够增加具备值和优先级的新我的项目。在出队时,PriorityQueue 返回具备最低优先级值的元素。您能够认为这个新汇合相似于 Queue<T> 但每个入队元素都有一个影响出队行为的优先级值。
以下示例演示了.PriorityQueue<string, int>
// creates a priority queue of strings with integer priorities
var pq = new PriorityQueue<string, int>();
// enqueue elements with associated priorities
pq.Enqueue(“A”, 3);
pq.Enqueue(“B”, 1);
pq.Enqueue(“C”, 2);
pq.Enqueue(“D”, 3);
pq.Dequeue(); // returns “B”
pq.Dequeue(); // returns “C”
pq.Dequeue(); // either “A” or “D”, stability is not guaranteed.
归功于 Patryk Golebiowski。
更快地将构造解决为字典值
CollectionsMarshal.GetValueRef 是一个新的不平安 API,它能够更快地更新字典中的构造值。新 API 旨在用于高性能场景,而不是用于个别用处。它返回 ref 构造值,而后能够应用典型技术对其进行更新。
以下示例演示了如何应用新 API:
ref MyStruct value = CollectionsMarshal.GetValueRef(dictionary, key); // Returns Unsafe.NullRef<TValue>() if it doesn’t exist; check using Unsafe.IsNullRef(ref value) if (!Unsafe.IsNullRef(ref value)) {// Mutate in-place value.MyInt++;}
在此更改之前,更新 struct 字典值对于高性能场景可能会很低廉,须要字典查找和复制到堆栈的 struct. 而后在更改之后 struct,它将再次调配给字典键,从而导致另一个查找和复制操作。这种改良将密钥散列缩小到 1(从 2)并删除了所有构造复制操作。
归功于本亚当斯。
新建 DateOnly 和 TimeOnly 构造
增加了仅限日期和工夫的构造,具备以下特色:
- 每个都代表 a 的一半 DateTime,或者只是日期局部,或者只是工夫局部。
- DateOnly 非常适合生日、周年纪念日和工作日。它与 SQL Server 的 date 类型统一。
- TimeOnly 非常适合定期会议、闹钟和每周工作工夫。它与 SQL Server 的 time 类型统一。
- 补充现有的日期 / 工夫类型 (DateTime, DateTimeOffset, TimeSpan, TimeZoneInfo)。
- 在 System 命名空间中,在 CoreLib 中提供,就像现有的相干类型一样。
性能改良 DateTime.UtcNow
这种改良具备以下益处:
- 修复了在 Windows 上获取零碎工夫的 2.5 倍性能回归。
- 利用 Windows 闰秒数据的 5 分钟滑动缓存,而不是在每次调用时获取。
- 在所有平台上反对 Windows 和 IANA 时区
这种改良具备以下益处:
- 应用时的隐式转换(https://github.com/dotnet/run…)TimeZoneInfo.FindSystemTimeZoneById
- TimeZoneInfo 通过: TryConvertIanaIdToWindowsId、TryConvertWindowsIdToIanaId 和 HasIanaId(https://github.com/dotnet/run…)上的新 API 进行显式转换
- 改良了应用不同时区类型的零碎之间的跨平台反对和互操作。
- 删除须要应用 TimeZoneConverter OSS 库。该性能当初是内置的。
改良的时区显示名称
Unix 上的时区显示名称已失去改良:
- 打消由 . 返回的列表中的显示名称的歧义。TimeZoneInfo.GetSystemTimeZones
- 利用 ICU / CLDR 全球化数据。
- 仅实用于 Unix。Windows 依然应用注册表数据。这可能会在当前更改。
- 还进行了以下附加改良:
- UTC 时区的显示名称和规范名称被硬编码为英语,当初应用与其余时区数据雷同的语言(CurrentUICulture 在 Unix 上,Windows 上的操作系统默认语言)。
- 因为大小限度,Wasm 中的时区显示名称改为应用非本地化 IANA ID。
- TimeZoneInfo.AdjustmentRule 嵌套类将其 BaseUtcOffsetDelta 外部属性公开,并取得一个新的构造函数,该构造函数 baseUtcOffsetDelta 作为参数。(https://github.com/dotnet/run…)
- TimeZoneInfo.AdjustmentRule 还取得了在 Unix 上加载时区的各种修复 (https://github.com/dotnet/run…), (https://github.com/dotnet/run…)
改良了对 Windows ACL 的反对
System.Threading.AccessControl 当初包含对与 Windows 访问控制列表 (ACL) 交互的改良反对。新的重载被增加到 Mutex 和 Semaphore 的 OpenExisting 和 TryOpenExisting 办法 EventWaitHandle 中。这些具备“平安权限”实例的重载容许关上应用非凡 Windows 平安属性创立的线程同步对象的现有实例。
此更新与 .NET Framework 中可用的 API 匹配并且具备雷同的行为。
以下示例演示了如何应用这些新 API。
对于 Mutex:
var rights = MutexRights.FullControl; string mutexName = “MyMutexName”; var security = new MutexSecurity(); SecurityIdentifier identity = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null); MutexAccessRule accessRule = new MutexAccessRule(identity, rights, AccessControlType.Allow); security.AddAccessRule(accessRule); // createdMutex, openedMutex1 and openedMutex2 point to the same mutex Mutex createdMutex = MutexAcl.Create(initiallyOwned: true, mutexName, out bool createdNew, security); Mutex openedMutex1 = MutexAcl.OpenExisting(mutexName, rights); MutexAcl.TryOpenExisting(mutexName, rights, out Mutex openedMutex2);
为了 Semaphore
var rights = SemaphoreRights.FullControl; string semaphoreName = “MySemaphoreName”; var security = new SemaphoreSecurity(); SecurityIdentifier identity = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null); SemaphoreAccessRule accessRule = new SemaphoreAccessRule(identity, rights, AccessControlType.Allow); security.AddAccessRule(accessRule); // createdSemaphore, openedSemaphore1 and openedSemaphore2 point to the same semaphore Semaphore createdSemaphore = SemaphoreAcl.Create(initialCount: 1, maximumCount: 3, semaphoreName, out bool createdNew, security); Semaphore openedSemaphore1 = SemaphoreAcl.OpenExisting(semaphoreName, rights); SemaphoreAcl.TryOpenExisting(semaphoreName, rights, out Semaphore openedSemaphore2);
为了 EventWaitHandle
var rights = EventWaitHandleRights.FullControl; string eventWaitHandleName = “MyEventWaitHandleName”; var security = new EventWaitHandleSecurity(); SecurityIdentifier identity = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null); EventWaitHandleAccessRule accessRule = new EventWaitHandleAccessRule(identity, rights, AccessControlType.Allow); security.AddAccessRule(accessRule); // createdHandle, openedHandle1 and openedHandle2 point to the same event wait handle EventWaitHandle createdHandle = EventWaitHandleAcl.Create(initialState: true, EventResetMode.AutoReset, eventWaitHandleName, out bool createdNew, security); EventWaitHandle openedHandle1 = EventWaitHandleAcl.OpenExisting(eventWaitHandleName, rights); EventWaitHandleAcl.TryOpenExisting(eventWaitHandleName, rights, out EventWaitHandle openedHandle2);
HMAC 一次性办法
System.Security.Cryptography HMAC 类当初具备容许一次性计算 HMAC 而无需调配的静态方法。这些增加相似于在先前版本中增加的用于哈希生成的一次性办法。
DependentHandle 现已公开
该 DependentHandle 类型当初是公共的,具备以下 API 外表:
namespace System.Runtime {public struct DependentHandle : IDisposable { public DependentHandle(object? target, object? dependent); public bool IsAllocated {get;} public object? Target {get; set;} public object? Dependent {get; set;} public (object? Target, object? Dependent) TargetAndDependent {get;} public void Dispose();} }
它可用于创立高级零碎,例如简单的缓存零碎或 ConditionalWeakTable<TKey, TValue> 类型的自定义版本。例如,它将被 MVVM Toolkit 中的 WeakReferenceMessenger 类型应用,以防止在播送音讯时分配内存。
可移植线程池
.NET 线程池已作为托管实现从新实现,当初用作 .NET 6 中的默认线程池。咱们进行此更改以使所有 .NET 应用程序都能够拜访同一个线程池,而不论是否正在应用 CoreCLR、Mono 或任何其余运行时。作为此更改的一部分,咱们没有察看到或预期任何性能或性能影响。
RyuJIT
该团队在此版本中对 .NET JIT 编译器进行了许多改良,在每个预览帖子中都有记录。这些更改中的大多数都进步了性能。这里介绍了一些 RyuJIT 的亮点。
动静 PGO
在 .NET 6 中,咱们启用了两种模式的 PGO(配置文件疏导优化):
- 动静 PGO 应用从以后运行中收集的数据来优化以后运行。
- 动态 PGO 依附从过来运行中收集的数据来优化将来运行。
动静 PGO 曾经在文章后面的性能局部中介绍过。我将提供一个从新下限。
动静 PGO 使 JIT 可能在运行时收集无关理论用于特定利用程序运行的代码门路和类型的信息。而后,JIT 能够依据这些代码门路优化代码,有时会显着进步性能。咱们在测试和生产中都看到了两位数的衰弱改良。有一组经典的编译器技术在没有 PGO 的状况下应用 JIT 或提前编译都无奈实现。咱们当初可能利用这些技术。热 / 冷拆散是一种这样的技术,而去虚拟化是另一种技术。
要启用动静 PGO,请在应用程序将运行的环境中进行设置 DOTNET_TieredPGO=1。
如性能局部所述,动静 PGO 将 TechEmpower JSON“MVC”套件每秒的申请数进步了 26%(510K -> 640K)。这是一个惊人的改良,无需更改代码。
咱们的指标是在将来的 .NET 版本中默认启用动静 PGO,心愿在 .NET 7 中启用。咱们强烈建议您在应用程序中尝试动静 PGO 并向咱们提供反馈。
残缺的 PGO
要充分利用 Dynamic PGO,您能够设置两个额定的环境变量:DOTNET_TC_QuickJitForLoops= 1 和 DOTNET_ReadyToRun=0. 这确保了尽可能多的办法参加分层编译。咱们将此变体称为 Full PGO。与动静 PGO 相比,残缺 PGO 能够提供更大的稳态性能劣势,但启动工夫会更慢(因为必须在第 0 层运行更多办法)。
您不心愿将此选项用于短期运行的无服务器应用程序,但对于长期运行的应用程序可能有意义。
在将来的版本中,咱们打算精简和简化这些选项,以便您能够更简略地取得残缺 PGO 的益处并用于更宽泛的应用程序。
动态 PGO
咱们目前应用动态 PGO 来优化 .NET 库程序集,例如 R2R(Ready To Run)附带的程序集 System.Private.CoreLib。
动态 PGO 的益处是,在应用 crossgen 将程序集编译为 R2R 格局时会进行优化。这意味着有运行时的益处而没有运行时老本。这是十分重要的,也是 PGO 对 C++ 很重要的起因,例如。
循环对齐
内存对齐是古代计算中各种操作的独特要求。在 .NET 5 中,咱们开始在 32 字节边界对齐办法。在 .NET 6 中,咱们增加了一项执行自适应循环对齐的性能,该性能在具备循环的办法中增加 NOP 填充指令,以便循环代码从 mod(16) 或 mod(32) 内存地址开始。这些更改改良并稳固了 .NET 代码的性能。
在上面的冒泡排序图中,数据点 1 示意咱们开始在 32 字节边界对齐办法的点。数据点 2 示意咱们也开始对齐外部循环的点。如您所见,基准测试的性能和稳定性都有很大进步。
硬件加速构造
构造是 CLR 类型零碎的重要组成部分。近年来,它们常常被用作整个 .NET 库中的性能原语。最近的例子 ValueTask 是 ValueTuple 和 Span<T>。记录构造是一个新的例子。在 .NET 5 和 .NET 6 中,咱们始终在进步构造的性能,局部起因是通过确保构造是局部变量、参数或办法的返回值时能够保留在超疾速 CPU 寄存器中)。这对于应用向量计算的 API 特地有用。
稳定性能测量
团队中有大量从未呈现在博客上的工程零碎工作。这对于您应用的任何硬件或软件产品都是如此。JIT 团队发展了一个我的项目来稳定性能测量,指标是减少咱们外部性能实验室自动化主动报告的回归值。这个我的项目很乏味,因为须要进行深入调查和产品更改能力实现稳定性。它还展现了咱们为放弃和进步绩效而掂量的规模。
此图像演示了不稳固的性能测量,其中性能在间断运行中在慢速和疾速之间稳定。x 轴是测试日期,y 轴是测试工夫,以纳秒为单位。到图表开端(提交这些更改后),您能够看到测量值稳固,后果最好。这张图片展现了一个繁多的测试。还有更多测试在 dotnet/runtime #43227 中被证实具备相似的行为。
即用型代码 /Crossgen 2
Crossgen2 是 crossgen 工具的替代品。它旨在满足两个后果:
- 让 crossgen 开发更高效。
- 启用一组目前无奈通过 crossgen 实现的性能。
这种转换有点相似于本机代码 csc.exe 到托管代码 Roslyn 编译器。Crossgen2 是用 C# 编写的,然而它没有像 Roslyn 那样公开一个花哨的 API。
咱们可能曾经 / 曾经为 .NET 6 和 7 打算了六个我的项目,这些我的项目依赖于 crossgen2。矢量指令默认提议是咱们心愿为 .NET 6 但更可能是 .NET 7 进行的 crossgen2 性能和产品更改的一个很好的例子。版本气泡是另一个很好的例子。
Crossgen2 反对跨操作系统和架构维度的穿插编译(因而称为“crossgen”)。这意味着您将可能应用单个构建机器为所有指标生成本机代码,至多与筹备运行的代码相干。然而,运行和测试该代码是另一回事,为此您须要适合的硬件和操作系统。
第一步是用 crossgen2 编译平台自身。咱们应用 .NET 6 实现了所有架构的工作。因而,咱们可能在此版本中淘汰旧的 crossgen。请留神,crossgen2 仅实用于 CoreCLR,而不适用于基于 Mono 的应用程序(它们具备一组独自的代码生成工具)。
这个我的项目——至多一开始——并不以性能为导向。指标是启用更好的架构来托管 RyuJIT(或任何其余)编译器以离线形式生成代码(不须要或启动运行时)。
你可能会说“嘿……如果是用 C# 编写的,难道你不须要启动运行时来运行 crossgen2 吗?”是的,但这不是本文中“离线”的含意。当 crossgen2 运行时,咱们不应用运行 crossgen2 的运行时附带的 JIT 来生成筹备运行 (R2R) 代码. 那是行不通的,至多对于咱们的指标来说是行不通的。设想一下 crossgen2 在 x64 机器上运行,咱们须要为 Arm64 生成代码。Crossgen2 将 Arm64 RyuJIT(针对 x64 编译)加载为原生插件,而后应用它生成 Arm64 R2R 代码。机器指令只是保留到文件中的字节流。它也能够在相同的方向工作。在 Arm64 上,crossgen2 能够应用编译为 Arm64 的 x64 RyuJIT 生成 x64 代码。咱们应用雷同的办法来针对 x64 机器上的 x64 代码。Crossgen2 会加载一个 RyuJIT,它是为任何须要的配置而构建的。这可能看起来很简单,但如果您想启用无缝的穿插定位模型,它就是您须要的那种零碎,而这正是咱们想要的。
咱们心愿只在一个版本中应用术语“crossgen2”,之后它将替换现有的 crossgen,而后咱们将回到应用术语“crossgen”来示意“crossgen2”。
.NET 诊断:EventPipe
EventPipe 是咱们用于在过程内或过程外输入事件、性能数据和计数器的跨平台机制。从 .NET 6 开始,咱们已将实现从 C++ 移至 C。通过此更改,Mono 也应用 EventPipe。这意味着 CoreCLR 和 Mono 都应用雷同的事件基础设施,包含 .NET 诊断 CLI 工具。
这一变动还随同着 CoreCLR 的小幅减小:
咱们还进行了一些更改,以进步 EventPipe 在负载下的吞吐量。在最后的几个预览版中,咱们进行了一系列更改,从而使吞吐量进步了 .NET 5 的 2.06 倍:
对于这个基准,越高越好。.NET 6 是橙色线,.NET 5 是蓝色线。
SDK
对 .NET SDK 进行了以下改良。
.NET 6 SDK 可选工作负载的 CLI 装置
.NET 6 引入了 SDK 工作负载的概念。工作负载是可选组件,能够装置在 .NET SDK 之上以启用各种场景。.NET 6 中的新工作负载是:.NET MAUI 和 Blazor WebAssembly AOT 工作负载。咱们可能会在 .NET 7 中创立新的工作负载(可能来自现有的 SDK)。工作负载的最大益处是缩小大小和可选性。咱们心愿随着工夫的推移使 SDK 变得更小,并且只装置您须要的组件。这个模型对开发者机器有益处,对 CI 来说甚至更好。
Visual Studio 用户并不真正须要放心工作负载。工作负载性能通过专门设计,以便像 Visual Studio 这样的装置协调器能够为您装置工作负载。能够通过 CLI 间接管理工作负载。
工作负载性能公开了用于管理工作负载的多个动词,包含以下几个:
- dotnet workload restore— 装置给定我的项目所需的工作负载。
- dotnet workload install— 装置命名工作负载。
- dotnet workload list— 列出您已装置的工作负载。
- dotnet workload update— 将所有已装置的工作负载更新到最新的可用版本。
update 动词查问更新 nuget.org 的工作负载清单、更新本地清单、下载已装置工作负载的新版本,而后删除所有旧版本的工作负载。这相似于 apt update && apt upgrade -y(用于基于 Debian 的 Linux 发行版)。将工作负载视为 SDK 的公有包管理器是正当的。它是公有的,因为它仅实用于 SDK 组件。咱们未来可能会重新考虑这一点。这些 dotnet workload 命令在给定 SDK 的上下文中运行。假如您同时装置了 .NET 6 和 .NET 7。工作负载命令将为每个 SDK 提供不同的后果,因为工作负载将不同(至多雷同工作负载的不同版本)。
请留神,将 NuGet.org 中的工作负载复制到您的 SDK 装置中,因而如果 SDK 装置地位受到爱护(即在管理员 / 根地位),dotnet workload install 则须要运行晋升或应用 sudo。
内置 SDK 版本查看
为了更容易跟踪 SDK 和运行时的新版本何时可用,咱们向 .NET 6 SDK 增加了一个新命令。
dotnet sdk check
它会告诉您是否有可用于您已装置的任何 .NET SDK、运行时或工作负载的更新版本。您能够在下图中看到新体验。
dotnet new
您当初能够在 NuGet.org 中搜寻带有.dotnet new –search
模板装置的其余改良包含反对切换以反对公有 NuGet 源的受权凭据。–interactive
装置 CLI 模板后,您能够通过和查看更新是否可用。–update-check–update-apply
NuGet 包验证
包验证工具使 NuGet 库开发人员可能验证他们的包是否统一且格局正确。
这包含:
- 验证版本之间没有重大更改。
- 验证包对于所有特定于运行时的实现是否具备雷同的公共 API 集。
- 确定任何指标框架或运行时适用性差距。
该工具是 SDK 的一部分。应用它的最简略办法是在我的项目文件中设置一个新属性。
<EnablePackageValidation> true </EnablePackageValidation>
更多 Roslyn 分析仪
在 .NET 5 中,咱们提供了大概 250 个带有 .NET SDK 的分析器。其中许多曾经存在,但作为 NuGet 包在带外发送。咱们为 .NET 6 增加了更多分析器。
默认状况下,大多数新分析器都在信息级别启用。您能够通过如下配置剖析模式在正告级别启用这些分析器:<AnalysisMode>All</AnalysisMode>
咱们为 .NET 6 公布了咱们想要的一组分析器(加上一些附加性能),而后将它们中的大多数做成了可供抓取的。社区增加了几个实现,包含这些。
感激 Meik Tranel 和 Newell Clark。
为 Platform Compatibility Analyzer 启用自定义防护
CA1416 平台兼容性分析器曾经应用 OperatingSystem 和 RuntimeInformation 中的办法辨认平台防护,例如 OperatingSystem.IsWindows 和 OperatingSystem.IsWindowsVersionAtLeast。然而,分析器无奈辨认任何其余爱护可能性,例如缓存在字段或属性中的平台查看后果,或者在辅助办法中定义了简单的平台查看逻辑。
为了容许自定义守卫的可能性,咱们增加了新属性 SupportedOSPlatformGuard 并 UnsupportedOSPlatformGuard 应用相应的平台名称和 / 或版本正文自定义守卫成员。此正文被平台兼容性分析器的流剖析逻辑辨认和尊重。
用法
[UnsupportedOSPlatformGuard("browser")] // The platform guard attribute
#if TARGET_BROWSER
internal bool IsSupported => false;
#else
internal bool IsSupported => true;
#endif
[UnsupportedOSPlatform("browser")]
void ApiNotSupportedOnBrowser() {}
void M1()
{ApiNotSupportedOnBrowser(); // Warns: This call site is reachable on all platforms.'ApiNotSupportedOnBrowser()' is unsupported on: 'browser'
if (IsSupported)
{ApiNotSupportedOnBrowser(); // Not warn
}
}
[SupportedOSPlatform("Windows")]
[SupportedOSPlatform("Linux")]
void ApiOnlyWorkOnWindowsLinux() {}
[SupportedOSPlatformGuard("Linux")]
[SupportedOSPlatformGuard("Windows")]
private readonly bool _isWindowOrLinux = OperatingSystem.IsLinux() || OperatingSystem.IsWindows();
void M2()
{ApiOnlyWorkOnWindowsLinux(); // This call site is reachable on all platforms.'ApiOnlyWorkOnWindowsLinux()' is only supported on: 'Linux', 'Windows'.
if (_isWindowOrLinux)
{ApiOnlyWorkOnWindowsLinux(); // Not warn
}
}
}
完结
欢送应用 .NET 6。它是另一个微小的 .NET 版本,在性能、性能、可用性和安全性方面都有很多的改良。咱们心愿您能找到许多改良,最终使您在日常开发中更有效率和能力,并进步性能或降低生产中应用程序的老本。咱们曾经开始从那些曾经开始应用 .NET 6 的人那里听到好消息。
在 Microsoft,咱们还处于 .NET 6 部署的晚期阶段,一些要害应用程序曾经投入生产,将来几周和几个月内还会有更多应用程序推出。
.NET 6 是咱们最新的 LTS 版本。咱们激励每个人都转向它,特地是如果您应用的是 .NET 5。咱们期待它成为有史以来采纳速度最快的 .NET 版本。
感谢您成为 .NET 开发人员。
点击理解更多.NET 6