欢送应用.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 -\&gt; 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 的后果。

办法意思是代码大小
PGO 已禁用1.905 纳秒30乙
启用PGO0.7071 纳秒105乙

这是一个相当大的差别,但代码大小也有所增加,这可能会让一些读者感到诧异。这是由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倍的调配降落

办法运行意思是比率已调配
写100MBAsync.NET 5.01,308.2 毫秒1.003,809 KB
写100MBAsync.NET 6.0306.8 毫秒0.243 KB

咱们还意识到须要更高性能的文件 IO 性能:并发读取和写入,以及扩散/收集 IO。 针对这些状况,咱们为 System.IO.FileSystem.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倍

办法意思是标准差比率
串行器243.1 纳秒9.54 纳秒1.00
SrcGenSerializer149.3 纳秒1.91 纳秒0.62

TechEmpower缓存基准测试平台或框架对来自数据库的信息进行内存缓存。基准测试的.NET 实现执行缓存数据的JSON 序列化,以便将其作为响应发送到测试工具。

申请 / 要求
net5.0243,0003,669,151
网6.0260,9283,939,804
net6.0 + JSON 源码生成364,2245,499,468

咱们察看到约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\&gt;enable\&lt;/ImplicitUsings>

隐式应用对于现有我的项目是可选的,但默认蕴含在新C# 我的项目中。无关详细信息,请参阅隐式应用。

文件范畴的命名空间

文件范畴的命名空间使您可能申明整个文件的命名空间,而无需将残余内容嵌套在{ ...}中. 只容许一个,并且必须在申明任何类型之前呈现。

新语法是单个的一行:

namespaceMyNamespace;classMyClass{...}// Not indented

这种新语法是三行缩进款式的代替计划:

namespaceMyNamespace{classMyClass{...}// Everything is indented}

益处是在整个文件位于同一个命名空间中的极其常见的状况下缩小缩进。

记录构造

C# 9 将记录作为一种非凡的面向值的类模式引入。在C# 10 中,您还能够申明构造记录。C# 中的构造曾经具备值相等,但记录构造增加了==运算符和IEquatable<T>的实现,以及基于值的ToString实现:

public record structPerson{publicstringFirstName{get; init;}publicstringLastName{get; init;}}

就像记录类一样,记录构造能够是"地位的",这意味着它们有一个主构造函数,它隐式申明与参数对应的公共成员:

public record structPerson(stringFirstName,stringLastName);

然而,与记录类不同,隐式公共成员是_可变的主动实现的属性_。这样一来,记录构造就成为了元组的天然成长故事。例如,如果您有一个返回类型(string FirstName, string LastName),并且您心愿将其扩大为命名类型,您能够轻松地申明相应的地位构造记录并保护可变语义。

如果你想要一个具备只读属性的不可变记录,你能够申明整个记录构造readonly(就像你能够其余构造一样):

publicreadonly record structPerson(stringFirstName,stringLastName);

C# 10 不仅反对记录构造,还反对_所有_构造以及匿名类型的with表达式:

var updatedPerson = person with{FirstName=&quot;Mary&quot;};

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-runtimeinfoYou can invoke the tool using the following command: dotnet-runtimeinfoTool '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, MM42MMMa8"    `Y42 a8"     "8a  42    42P'   `"8a a8P_____42   428b       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 informationVersion: 6.0.0FrameworkDescription: .NET 6.0.0-rtm.21522.10Libraries version: 6.0.0-rtm.21522.10Libraries hash: 4822e3c3aa77eb82b2fb33c9321f923cf11ddde6**Environment informationProcessorCount: 8OSArchitecture: Arm64OSDescription: Microsoft Windows 10.0.22494OSVersion: Microsoft Windows NT 10.0.22494.0

如您所见,该工具在Windows Arm64 上本机运行。我将向您展现ASP.NET Core 的样子。

macOS Arm64

您能够看到在macOS Arm64 上的体验是类似的,并且还展现了架构指标。

rich@MacBook-Air app % dotnet --version6.0.100rich@MacBook-Air app % dotnet --info | grep RID RID:         osx-arm64rich@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 runHello, Arm64 from .NET 6.0.0-rtm.21522.10!rich@MacBook-Air app % dotnet run -a x64Hello, 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:aspnetappwarn: 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://[::]:80info: Microsoft.Hosting.Lifetime[0]      Application started. Press Ctrl+C to shut down.info: Microsoft.Hosting.Lifetime[0]      Hosting environment: Productioninfo: 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 公开了四个工具类:CounterHistogramObservableCounter和,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 包含更好的控件可拜访性、设置应用程序范畴的默认字体、模板更新等的能力。

可拜访性改良

在此版本中,咱们增加了用于CheckedListBoxLinkLabelPanelScrollBarTabControlTrackBar的UIA 提供程序,它们使讲述人等工具和测试自动化可能与应用程序的元素进行交互。

默认字体

您当初能够应用.Application.SetDefaultFont

voidApplication.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(newFont(...));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增加了正告。

压缩

单文件包当初反对压缩,能够通过将属性设置EnableCompressionInSingleFiletrue. 在运行时,文件会依据须要解压缩到内存中。压缩能够为某些场景节俭大量空间。

让咱们看一下与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用于同时计算SinCos
  • ReciprocalEstimate用于计算 1 / x的近似值。
  • ReciprocalSqrtEstimate用于计算1 / Sqrt(x)的近似值。

新的重载:

  • Clamp, DivRem,MinMax反对nintnuint
  • AbsSign反对nint
  • DivRem 变体返回tuple

性能改良:

  • ScaleB被移植到C# 导致调用速度进步了93%。感激亚历克斯·科文顿。

大整数性能

改良了从十进制和十六进制字符串中解析 BigIntegers。咱们看到了高达89% 的改良,如下图所示(越低越好)。

感激约瑟夫·达席尔瓦。

Complex API 当初正文为 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>已增加到Vector2Vector3Vector4以改善须要加载或存储矢量类型时的体验。

更好地解析规范数字格局

咱们改良了规范数字类型的解析器,特地是.ToString.TryFormatParse。他们当初将了解对精度 >99 位小数的要求,并将为那么多位数提供精确的后果。此外,解析器当初更好地反对办法中的尾随零。

以下示例演示了之前和之后的行为。

  • 32.ToString("C100")->C132

    • .NET 6:<details>
      </details>$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.ReflectionSystem.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 chainedDebug.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 InvalidOperationExceptionusing (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

MaxByMinBy办法容许应用键选择器查找最大或最小元素,如下例所示。

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\&lt;int\&gt;().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 prioritiesvar pq = new PriorityQueue<string, int>();// enqueue elements with associated prioritiespq.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通过: TryConvertIanaIdToWindowsIdTryConvertWindowsIdToIanaIdHasIanaId(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) 交互的改良反对。新的重载被增加到MutexSemaphoreOpenExistingTryOpenExisting办法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 mutexMutex 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 semaphoreSemaphore 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 handleEventWaitHandle 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.CryptographyHMAC类当初具备容许一次性计算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 -\&gt; 640K)。这是一个惊人的改良,无需更改代码。

咱们的指标是在将来的.NET 版本中默认启用动静PGO,心愿在.NET 7 中启用。咱们强烈建议您在应用程序中尝试动静PGO 并向咱们提供反馈。

残缺的 PGO

要充分利用Dynamic PGO,您能够设置两个额定的环境变量:DOTNET\_TC\_QuickJitForLoops=1DOTNET\_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 库中的性能原语。最近的例子ValueTaskValueTupleSpan<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 的小幅减小:

大小之后 - 大小之前差别
libcoreclr.so7037856 – 7049408-11552

咱们还进行了一些更改,以进步 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 &amp;&amp; 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 公布了咱们想要的一组分析器(加上一些附加性能),而后将它们中的大多数做成了可供抓取的。社区增加了几个实现,包含这些。

贡献者问题题目
纽厄尔·克拉克dotnet/运行时#33777应用基于跨度的string.Concat
纽厄尔·克拉克dotnet/运行时#33784解析时优先string.AsSpan()string.Substring()
纽厄尔·克拉克dotnet/运行时#33789笼罩Stream.ReadAsync/WriteAsync
纽厄尔·克拉克dotnet/运行时#35343替换为Dictionary\&lt;,\&gt;.Keys.ContainsContainsKey
纽厄尔·克拉克dotnet/运行时#45552应用代替String.EqualsString.Compare
梅克特雷尔dotnet/运行时#47180应用代替String.Contains(char)String.Contains(String)

感激Meik Tranel和Newell Clark。

为 Platform Compatibility Analyzer 启用自定义防护

CA1416 平台兼容性分析器曾经应用OperatingSystemRuntimeInformation中的办法辨认平台防护,例如OperatingSystem.IsWindowsOperatingSystem.IsWindowsVersionAtLeast。然而,分析器无奈辨认任何其余爱护可能性,例如缓存在字段或属性中的平台查看后果,或者在辅助办法中定义了简单的平台查看逻辑。

为了容许自定义守卫的可能性,咱们增加了新属性 SupportedOSPlatformGuardUnsupportedOSPlatformGuard应用相应的平台名称和/或版本正文自定义守卫成员。此正文被平台兼容性分析器的流剖析逻辑辨认和尊重。

用法

    [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 版本。

此版本是至多1000 人(但可能更多)的后果。这包含来自Microsoft 的.NET 团队以及社区中的更多人。我试图在这篇文章中蕴含许多社区奉献的性能。感谢您抽出宝贵时间创立这些内容并实现咱们的流程。我心愿这次经验是一次美妙的经验,并且更多的人会做出奉献。

这篇文章是许多有才华的人单干的后果。奉献包含团队在整个公布过程中提供的性能内容、为此最终帖子创立的重要新内容,以及使最终内容达到您应得的品质所需的大量技术和散文更正。很快乐为您制作它和所有其余帖子。

感谢您成为.NET 开发人员。