.NET Core 3.0 正式公布:新特性详细解读

近日,.NET Core 3.0 正式发布。此次新版本包含一系列重要改进,添加了 Windows Forms 与 WPF、添加新的 JSON API、对 ARM64 的支持能力以及全面提高的性能水平等。此外,C# 8 也是此次新版本的重要组成部分,其中包含可空、异步流以及更多模式。F# 4.7 则专注于放宽语法限制,并专门匹配 .NET Standard 2.0。开发者现在可以立即将原有项目更新为 .NET Core 3.0,此次新版本与原有版本兼容,大家可以放心体验。

.NET Core 3.0正式公布:新特性详细解读

根据微软博客的介绍,开发者可以面向 Windows、MacOS 以及 Linux 等系统平台下载 .NET Core 3.0 

此外, ASP .NET Core 3.0  与  EF Core 3.0  也已经一同发布。 Visual Studio 2019 16.3  与  Visual Studio for Mac 8.3  亦同时发布,且需要更新才能确保 .NET Core 3.0 与 Visual Studio 的协同使用。.NET Core 3.0 为 Visual Studio 2019 16.3 中的组成部分,开发者可以选择直接升级至 Visual Studio 2019 16.3,从而立刻获取 .NET Core。

感谢所有为 .NET Core 3.0 做出贡献的朋友们!此次最新版本的发布源自数百位团队成员的努力,也包括技术社区的重大贡献。

发行说明:

3.0 版本,开发者需要了解什么?

在深入探究 .NET Core 3.0 中的全部新功能之前,我们首先需要强调几项关键性的改进与指导内容。以下是整理出的要点清单:

  • .NET Core 3.0 已经在 dot.net 以及 Bing.com 上托管了几个月,通过了一系列严苛的测试。众多其他微软团队也将很快在生产流程当中通过 .NET Core 3.0 部署一系列大型工作负载。
  • 多种组件的性能得到显著提升,感兴趣的朋友可以点击此处参阅 .NET Core 3.0 的性能改进说明。
  • C# 8 加入了异步流、范围 / 索引、更多模式以及可为空的引用类型。可为空意味着可以直接发现那些导致 NullReferenceException 问题的代码缺陷。框架库的最底层注释也已添加完成,以帮助了解何时为 Null。
  • F# 4.7 致力于通过隐式 yield 表达式及相关语法降低某些操作的实现难度。其中还包含对 LangVersion 的支持,提供 nameof 并可以预览形式打开静态类。F# Core 核心库现在还与 .NET Standard 2.0 相匹配。您可以点击此处参阅 F# 4.7 发布公告中的细节信息。
  • .NET Standard 2.1 增加了可与 .NET Core 以及 Xamarin 共同使用的代码类型集。.NET Standar 2.1 当中包含 .NET Core 2.1 以及之后版本中的所有类型。
  • Windows 桌面应用现已面向 Windows Forms 与 WPF(开源)得到 .NET Core 支持。其中,WPF 设计器为 Visual Studio 2019 16.3 版本中的组成部分。Windows Forms 设计器仍处于预览状态,并可通过VSIX 下载的形式获取。
  • .NET Core 应用现在默认具备可执行文件。在以往的发行版中,应用需要通过 dotnet 命令方启动,例如 dotnet myapp.dll。现在,可以通过应用特定可执行文件实现应用启动,例如 myapp 或者./myapp,具体视使用的操作系统而定。
  • 高性能 JSON API 加入新版本,适用于 reader/writer、对象模型以及序列化场景等。这些 API 在 Span基础之上重新构建而成,且在底层使用 UTF8(而非 string 等 UTF16)。这些 API 能够将分配需求控制在最低程度,从而提高性能、减少垃圾收集器的工作量。具体请参阅.NET Core 3.0 中的 JSON 未来发展说明。
  • 默认情况下,垃圾收集器的内存占用量得到了显著削减。对于将众多应用程序托管在同一服务器之上的使用场景,这项改进可谓意义重大。垃圾收集器本身也得到了更新,能够利用 64 核及以上设备的大量计算核心。
  • .NET Core 已针对 Docker 进行了增强,以使 .NET 应用程序能够在容器中以可预测的方式高效运作。在容器配置中的内存或 CPU 资源有限时,目前的垃圾收集器与线程池更新结果也能带来更好的运作效果。.NET Core docker 镜像也变得更小,其中 SDK 镜像的瘦身效果尤其明显。
  • Raspberry Pi 与 ARM 芯片现已得到支持,可配合远程 Visual Studio 调试程序等工具实现物联网开发。开发者可部署应用以监听各传感器,同时将消息或者图像输出至显示器上,整个过程皆可通过新的 GPIO API 实现。ASP.NET 则可用于将数据公布于 API 或者以站点的形式对物网设备进行配置。
  • .NET Core 3.0 即为“当前”版本,我们计划在 2019 年 11 月推出下一代 .NET Core 3.1 版本。.NET Core 3.1 将为长期支持(LTS)版本(周期至少为 3 年)。我们建议您首先采用 .NET Core 3.0,而后更新至 3.1 版,升级过程将非常轻松。
  • .NET Core 2.2 将于今年 12 月 23 日停止服务,具体情况请参阅 .NET Core 支持策略
  • .NET Core 3.0 将通过 RHEL 8 的红帽 Applicaltion Streams 交付,这也是我们与红帽公司多年合作的最新成果。
  • 对于希望在 Windows 上使用 .NET Core 3.0 的用户,将必须升级至 Visual Studio 2019 16.3。
  • 对于希望在 Mac 上使用 .NET Core 3.0 的用户,将必须升级至 Visual Studio for Mac 8.3。
  • Visual Studio Code 用户应始终使用最新版本的 C#扩展,以确保能够正常支持最新方案,包括与 .NET Core 3.0 的匹配。
  • .NET Core 3.0 的 Azure App Serivce 部署目前正在进行当中。
  • .NET Core 3.0 的 Azure Dev Ops 部署即将推出。我们将在准备就绪之后发布更新。

平台支持

.NET Core 3.0 将在以下操作系统平台上得到支持:

  • Alpine: 3.9+
  • Debian: 9+
  • openSUSE: 42.3+
  • Fedora: 26+
  • Ubuntu: 16.04+
  • RHEL: 6+
  • SLES: 12+
  • macOS: 10.13+
  • Windows Client: 7, 8.1, 10 (1607+)
  • Windows Server: 2012 R2 SP1+

备注:Windows Forms 与 WPF 应用只适用于 Windows 操作系统。

芯片支持情况:

  • x64,Windows、macOS 以及 Linux
  • x86,Windows
  • ARM32,Windows 与 Linux
  • ARM64,Linux (kernel 4.14+)

备注:请确保 .NET Core 3.0 ARM64 部署方案采用 Linux 内核 4.14 或者更新版本。例如,Ubuntu 18.04 能够满足这一条件,但 16.04 版本无法支持。

WPF 与 Windows Forms

开发者可以在 Windows 系统上利用 .NET Core 3 构建 WPF 与 Windows Forms 应用从项目起步之初,我们就制定了强大的兼容性目标,旨在保证桌面应用程序能够从 .NET Framework 轻松迁移至 .NET Core 当中。我们已经收到众多开发人员的反馈,了解到他们已经成功将自己的应用迁移至 .NET Core 3.0,且整个流程非常轻松便捷。从了尽可能降低对开发人员的影响,WPF 以及 Windows Forms 没有受到任何影响,其仍可在 .NET Core 上正常运行。事实上,项目本身进行了重大调整,但我们认为不对用户造成影响才是最好的调整方式。

下图所示为一款 .NET Core Windows Forms 应用:

.NET Core 3.0正式公布:新特性详细解读

Visual Studio 2019 16.3 已经支持创建能够匹配 .NET Core 的 WPF 应用。其中包括新的模板、经过更新的 XAML 设计器以及 XMAL Hot Reload。该设计器类似于现有 XAML 设计器(能够匹配.NETFramework),但二者在体验上仍然略有不同。

从技术层面来看,最大的区别在于面向 .NET Core 的设计器采用新的接口进程(wpfsurface.exe)以仅运行针对 .NET Core 版本的运行时代码。以往,.NET Framework WPF 设计器进程(xdesproc.exe)本身即为承载设计器的 WPF .NET Framework 进程;由于运行时不兼容,我们无法使用 WPF .NET Framework 进程(在本示例为中 Visual Studio)将两个版本的 .NET(即 .NET Framework 与 .NET Core)加载至同一进程当中。这意味着设计器在一定程度上(例如设计器扩展)无法以同样的方式运作。如果正在编写设计器扩展,我们建议认真阅读 XAML 设计器扩展迁移文档

下图所示为显示在新设计器当中的 WPF 应用:

.NET Core 3.0正式公布:新特性详细解读

Windows Forms 设计器目前仍处于预览状态,可以单独进行下载。其将在后续更高版本内被添加至 Visual Studio 当中。该设计器的当前预算版包含对最常用控件以及底层功能的支持。我们将通过每月更新不断对该设计器做出更新。我们目前不建议大家将自己的 Windows Forms 应用程序移植至 .NET Core,特别是在高度依赖设计器的情况下。希望开发者体验目前的设计器预览版,并积极向我们反馈问题。

另外,大家也可以利用 .NET CLI 通过命令行进行桌面应用程序的创建与构建。
例如,您可以通过以下命令快速创建一个新的 Windows Forms 应用:复制代码

dotnet new winforms -o myapp  cd myapp  dotnet run 

您也可以利用同样的流程创建 WPF 应用:复制代码

dotnet new wpf -o mywpfapp  cd mywpfapp  dotnet run     

早在 2018 年 12 月,我们就将 Windows Forms 与 WPF 转化为开源项目。很高兴看到,目前技术社区以及 Windows Forms 及 WPF 团队正共同努力以改进这两套 UI 框架。在 WPF 方面,我们最初在 GitHub 库中只拥有少量代码,但目前几乎全部 WPF 代码都已经发布至 GitHub。随着时间的推移,未来还将有更多组件出现。与其他 .NET Core 项目一样,这些新的库将成为 .NET Foundation 的一部分,并遵循 MIT 开源许可。

System.Windows.Forms.DataVisualization 软件包(包含图表控件)目前也适用于新的 .NET Core 版本。大家现在可以将这些控制添加到您的 .NET Core WinForms 应用程序当中。该图表控件的源代码可从 Github 上的 dotnet/winforms-datavisualization 处获取。控件本身已经进行了调整,以简化面向 .NET Core 3 的移植过程,但我们并不打算对其做出大规模更新。

Windows 原生互操作

Windows 以常规 API、COM 以及 WinRT 的形式提供丰富的原生 API。我们也从 .NET Core 1.0 时代起即提供对 P/Invoke 的支持;在此次 .NET Core 3.0 当中,我们进一步添加了 CoCreate COM API、主动 WinRT API,以及将托管代码以 COM 组件形式处理等功能。很多开发人员都就这些功能向我们提出申请,我们相信其正式推出也将给大家的日常工作带来巨大便利。

去年下半年,我们宣布已经设法通过 .NET Core 实现了 Excel 自动化。那绝对是个有趣的时刻。在底层,我们的展示利用到 COM 互操作机制,例如 NOPIA、对象等效性以及自定义编组器等等。现在,您可以在扩展示例当中亲自体验多种相关演示方案。

目前,.NET Core 3.0 只部分支持托管 C++ 与 WinRT 互操作,完整的支持能力将在 .NET Core 3.1 当中推出。

可空引用类型

C# 8.0 当中引入了可空与不可空两种新的引用类型,这意味着用户可以对引用类型变量的属性做出重要声明:

  • 某项引用不应为空。当变量不应为空时,则编译器会强制执行规则以确保安全地撤销针对空变量的引用,而无需预先检查引用目标是否为空。
  • 某项引用可以为空。当变量可以为空时,编译器会强制执行规则以确保您已经对所引用的变量是否为空做出检查。

相较于无法从引用变量的变量声明当中确定设计意图的早期 C#版本,此次发布的新功能具有明显的优势。通过添加可空这一全新引用类型,您将能够更明确地声明自己的设计意图,而编译器则可以帮助您正确地完成这一目标并及时发现代码中的错误。

接口成员的默认实现

目前,一旦接口发布完毕,您将无法对其做出任何变更:换言之,要向其中添加新成员,我们就必然会对现有界面的实现成员造成破坏。

在 C# 8.0 当中,大家可以为某一接口成员提供主体。结果就是,如果实现该接口的类没有实现该成员(可能是因为编写代码时还不存在该成员),那么调用代码将只能获得默认的实现效果。复制代码

interface ILogger  {           void Log(LogLevel level, string message);           void Log(Exception ex) => Log(LogLevel.Error, ex.ToString()); // New overload  }   class ConsoleLogger : ILogger  {           public void Log(LogLevel level, string message) { ... }           // Log(Exception) gets default implementation  }     

在本示例当中,ConsoleLogger 类并不需要实现对 ILogger 中的 Log(Exception) 重载,因为其是通过默认实现进行声明的。现在,只要为现有实现成员提供默认实现,您就可以随时向现有接口添加新的成员。

异步流

现在,您可以利用 IAsyncEnumerable对异步数据流进行 foreach。这一新的接口满足了众多开发人员的长久需求,也就是 IEnumerable的异步版本。该语言允许大家对任务进行 await foreach 以使用其元素。在产生方面,您的 yield return 条目可能会产生异步流。虽然听起来似乎比较复杂,但其实际上将在实践当中显著简化操作。

以下示例展示了异步流的产生与使用情况。其中的 foreach 声明为异步,其本身会利用 yield return 为调用程序产生一条异步流。这种利用 yield return 的模式亦是我们推出的异步流标准生产方式。复制代码

async IAsyncEnumerable<int> GetBigResultsAsync()  {          await foreach (var result in GetResultsAsync())       {                if (result > 20) yield return result;        }  }    

除了能够实现 await foreach 之外,大家也可以创建出异步迭代器,例如一个能够返回 IAsyncEnumerable/IAsyncEnumerator 并在其中同时实现 await 与 yield return 的迭代器。对于需要处理的对象,您也可以使用能够实现 Stream 以及 Timer 等多种框架类型的 IAsyncDisposable。

索引与范围

我们还创建了新的语法与类型,可用于描述索引器、数组元素访问或者可以直接进行数据访问的任何其他类型。其中包括对单一值(索引的通常定义)或者两个值(描述范围)的支持。

Index 是一种用于描述数组索引的新类型。您可以从通过 int 从零开始对 Index 进行计数,也可以使用 ^ 前缀运算符创建索引。在以下示例中,我们可以同时观察到这两种情况:复制代码

Index i1 = 3;  // number 3 from beginning  Index i2 = ^4; // number 4 from end  int[] a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };  Console.WriteLine($"{a[i1]}, {a[i2]}"); // "3, 6"  

Range 的概念基本相同,其由两个 Index 值构成,其一代表开始、其二代表结束,且可以用 x…y 的范围表达式编写。接下来,大家即可使用 Range 索引以生成底层数据的一个切片,具体如下所示:复制代码

var slice = a[i1..i2]; // { 3, 4, 5 }    

使用声明很多朋友可能已经厌倦了使用那些要求缩进代码的语句,对吧?没问题,现在不会了。您可以按以下形式编写代码,该代码将在当前语句块的作用域末尾添加 using 声明,而后将对象放置在其末尾。复制代码

<br />using System;  using System.Linq;  using System.Collections.Generic;  using static System.Console;  using System.IO;     namespace usingapp  {      class Program      {          static void Main()          {              var filename = "Program.cs";              var line = string.Empty;              var magicString = "magicString";                var file = new FileInfo(filename);              using var reader = file.OpenText();              while ((line = reader.ReadLine())!= null)              {                  if (line.Contains(magicString))                    {                       WriteLine("Found string");                       return;                  }              }                 WriteLine("String not found");          } // reader disposed here      }  }   

Switch 表达式

几乎每一位 C#用户都非常喜欢 switch 语句的概念,但却不太喜欢它的语法。C# 8 引入了 switch 表达式,该表达式可实现以下功能:

  • terser 语法
  • 由于其属于表达式,所以会返回一个值
  • 与模式匹配全面集成

Switch 表达式中的关键字为“infix”,意味着该关键字位于测试值(在第一个示例中为 0)与示例列表之间,这种形式与 Lambda 表达式非常相似。

第一个示例对各方法使用了 Lambda 语法,该语法能够与 switch 表达式良好集成,但并非必需。复制代码

static string Display(object o) => o switch  {      Point { X: 0, Y: 0 }         => "origin",      Point { X: var x, Y: var y } => $"({x}, {y})",      _                            => "unknown"  };    

在此示例中,共有两种模式在发挥作用。首先,0 会与 Point 类型模式匹配,而后再与{大括号}内的属性模式匹配。_ 用于描述丢弃模式,其与 switch 语句的默认模式想再。

大家也可以更进一步依赖元组解构与参数位置,具体如下所示:复制代码

static State ChangeState(State current, Transition transition, bool hasKey) =>      (current, transition) switch      {          (Opened, Close)              => Closed,          (Closed, Open)               => Opened,          (Closed, Lock)   when hasKey => Locked,          (Locked, Unlock) when hasKey => Closed,          _ => throw new InvalidOperationException($"Invalid transition")      };      

在本示例中,大家可以看到我们并不需要为每一种情况定义变量或者显式类型。相反,编译器可以将当前正在测试的元组与已经定义的元组进行匹配。

所有这些模式,使得我们能够编写出意图更加明确的声明性代码,而非单纯执行测试的过程代码。编译器能够负责实现无聊的过程代码,并保证其始终得到正确执行。

当然,在某些情况下,switch 语句的实际效果可能仍然优于新的 switch 表达式与模式中的语法形式。

速度更快的 JSON API

.NET Core 3.0 当中包含一个新的 JSON API 家族,其用于实现 reader/writer 场景、利用文档对象模型(DOM)实现随机访问外加一个序列化程序。大家可能已经非常熟悉如何使用 Json.NET 。新的 API 旨在满足大多数同类场景需求,但内存需求更低、执行速度更快。

简而言之,我们希望构建一个新的 JSON API,从而充分利用.NET Core 中的所有全新性能优势,并以此为基础全面提升性能水平。很明显,我们不可能在保持现有代码库(例如 Jason.NET )兼容性的同时达成这一目标。

下面,让我们分层对这一新的 API 家族做出阐述。

Utf8JsonReader

System.Text.Json.Utf8JsonReader 是种面向面向 UTF-8 编码 JSON 文本的高性能、低分配、纯转发读取器,负责从 ReadOnlySpan当中读取内容。Utf8JsonReader 是一种基础的低级类型,我们可以利用它构建自定义解析器与解串器。利用新的 Utf8JasonReader,对 JSON 负载的读取速度将可达到以往 Json.NET 中读取速度的两倍。另外,除非大家需要将 JSON 令牌实现为(UTF16)字符串,否则其不会进行分配。

Utf8JsonWriter

System.Text.Json.Utf8JsonWriter 提供一种高性能、非缓存、纯转发方式,可用于将 String、Int32 以及 DateTime 等常规.NET 类型写入为 UTF-8 编码 JSON 文本。与 reader 类似,writer 是一种基础低级类型,开发人员可以利用它构建自定义序列化程序。使用新的 Utf8JasonWriter,JSON 负载的编写速度将比 Json.NET 写入器高 30% 到 80%,且不进行分配。

JsonDocument

System.Text.Json.JsonDocument 能够对 JSON 数据进行解析,并建立只读文档对象模型(DOM)功能,用以查询该对象以支持随机访问与枚举。其建立在 Utf8JasonReader 基础之上,组成数据的 JSON 元素可以通过由 JsonDocument 类型(被称为 RootElement 属性)公开的 JsonElement 类型进行访问。JsonElement 当中包含 JSON 数组与对象枚举器,外加用于将 JSON 文本转换为常见.NET 类型的 API。利用 JsonDocument,对常规 JSON 负载进行解析并访问其中各成员的速度,一般可以达到以往 Json.NET 的 2 到 3 倍;而且由于数据本身大小合理(小于 1 MB),因此几乎不需要进行数据分配。

JSON 序列化器

System.Text.Json.JsonSerializer 层位于高性能 Utf8JsonReader 与 Utf8JsonWriter 之上。其负责对来自 JSON 的对象进行反序列化,并对指向 JSON 的对象进行序列化。内存分配得到严格控制,同时支持利用 Stream 以异步方式进行 JSON 读取与写入。

引入全新 SqlClient

SqlClient 是一款数据提供程序,您可以利用它直接利用 ADO .NET API 访问微软 SQL Server 以及 Azure SQL Database 等。SqlClient 已经正式发布,并通过 Microsoft.Data.SqlClient NuGet 工具包进行更新,且同时支持 .NET Freamework 与 .Net Core 应用程序。通过使用 NuGet,SQL 团队将能够更轻松地为 Freamework 与 .Net Core 用户提供更新。

ARM 与物联网支持

我们在本次发行版当中实现了对 Linux ARM64 的支持,这也是对此前 .NET Core 2.1 与 2.2 版本当中面向 Linux 与 Windows 提供 ARM32 支持能力的延续。虽然某些物联网工作负载能够发挥我们现有 x64 功能提供的优势,但仍有不少用户强烈要求发布 ARM 支持功能。现在这一功能已经落实到位,我们也在与计划进行大规模部署的客户开展广泛合作。

不少利用.NET 的物联网部署场景涉及边缘设备,且完全面向网络。其他一些场景则要求对硬件进行直接访问。在本次发行版中,我们添加了新的功能以在 Linux 上使用串行端口,同时可利用 Raspberry Pi 等设备上的数字引脚功能。该引脚使用多项协议。我们还添加了对 GPIO、PWM、I2C 以及 SPI 的支持,以支持读取传感器数据、以无线方式交互并将文本与图像输出至显示器等功能。

此功能属于以下软件包中的组成部分:

作为 GPIO(及其他同类协议)支持的一部分,我们审视了以往版本中的原有元素。我们发现其中的 C#与 Python API,在这两种场景下 API 都属于原生库上的打包器,而这些库则采用 GPL 许可。我们认为这种方法缺乏可持续性。因此,我们构建了 100% 纯 C#解决方案以实现这些协议,这意味着我们的 API 可以在支持 .NET Core 的任何环境下运作,利用 C#调试器(通过 sourcelink)进行调试,并支持多种底层 Linux 驱动程序(包括 sysfs、libgpoid 以及其它针对特定主板的驱动程序)。所有代码皆遵循 MIT 许可。与现有技术相比,我们认为这种新方法将给.NET 开发人员带来巨大的便利。

.NET Core 运行时前滚策略更新

现在,.NET Core 运行时(更具体地讲,运行时绑定程序)开始提供主版本的前滚选择策略。运行时绑定程序将默认启用面向补丁与次版本的前滚功能。我们决定公开一组更为广泛的策略,以帮助开发人员快速恢复可能存在的更新问题,但原有前滚操作并不受影响。

我们发布了一项名为 RollForward 的新属性,该属性能够接受以下值:

  • LatestPatch——前滚至最新补丁版本。其会禁用 Minor 策略。
  • Minor ——前滚至最早次版本以解决所需次版本缺失问题。如果该次版本存在,则 LatesPatch 策略即可起效。这也是系统中采用的默认策略。
  • Major ——前滚至最早主版本与最早次版本,以解决所需主版本缺失问题。如果该主版本存在,则随后使用 Minor 策略。
  • LatestMinor——前滚至最新次版本,即使存在所请求的次版本亦不受影响。
  • LatestMajor ——前滚至最新主版本,即使存在所请求的主版本亦不受影响。
  • Disable ——不进行前滚。仅绑定至指定版本。我们不建议大家在常规用途中使用这一选项,因为其会禁用前滚至最新补丁后版本的功能。仅建议您在测试环境中使用。

Docker 与 cgroup 限制

很多开发人员都在利用容器技术打包并运行自己的应用程序。其中的一大重要问题,在于容器的 CPU 或者内存等资源可能受到限制。我们早在 2017 年就实现了对内存限制功能的支持,遗憾的是,我们发现该实现的主动性不足以将容器环境以可靠方式保持在配置限制之下,意味着即使设置了内存限制(特别是小于 500 MB 的情况),应用程序仍然有可能出现 OOM 问题。我们已经在 .NET Core 3.0 当中修复了这一问题。这一改进意义重大,我们强烈建议使用 .NET Core Docker 的用户升级至 .NET Core 3.0。

Docker 资源限制功能构建在 cgroup 基础之上,而 cgroup 又是一项 Linux 内核功能。从运行时的角度来看,我们需要以 cgroup 基元为目标。

大家可以利用 docker run -m 参数来限制容器的可用内存,如下所示,此示例用于创建一个基于 Alpine 且内存容量为 4 MB 的容(而后输出内存限制):复制代码

C:\>docker run -m 4mb --rm alpine cat /sys/fs/cgroup/memory/memory.limit_in_bytes  4194304      

我们还添加了新的变更,以更好地支持 CPU 限制(–cpus)。其中包括更新运行时对十进制 CPU 值进行四舍五入的方式。如果将—cpus 设置为(足够)接近某个较小的整数(例如 1.499999999)的值,则运行时会对该值进行四舍五入(这种情况下将四舍五入为 1)。结果就是,运行时所使用的 CPU 数量低于需求,从而导致 CPU 资源不足。通过对该值进行直接进位,运行时在理论上会增加 OS 线程调度程序的压力,但即使是在最极端的情况下(–cpus=1.000000001,以往 其会被四舍五入为 1,但现在会直接进位为 2),我们也没有发现 CPU 出现任何性能下降。

下一步是确保线程池遵循 CPU 限制。线程池算法中有一部分负责计算 CPU 的繁忙时间,也就是计算可用 CPU 容量。通过在计算 CPU 繁忙时间当中考虑 CPU 限制,我们得以避免线程池在争用中执行的各种试探操作:一种方法可能尝试分配更多线程以增加 CPU 繁忙时间;但另一种方法则可能尝试分配更少线程,以避免不必要的线程浪费。

默认情况下降低 GC 堆大小

在致力于改善对 docker 内存限制支持能力的同时,我们也受到了启发,并着手对通用 GC 策略加以更新,旨在为更为广泛的应用程序的内存使用率带来改善(即使未运行在容器之内)。这些变更使得第 0 代预算分配能够更好地与现代处理器的缓存大小及缓存层级保持一致。

我们的团队成员 Damian Edwards 注意到,ASP.NET 基准测试的内存使用量减少了一半,但其它性能指标并未出现任何负面影响。这是一项惊人的进步!如他所言,这些将成为新的默认设置,且不需要对您的代码做出任何修改(只需采用 .NET Core 3.0 即可)。

当然,我们在 ASP.NET 基准测试中观察到的内存节约效果,可能代表 / 不代表您的实际体验。我们希望听到更多朋友在实际应用程序内存使用量方面观察到的结果。

改善对多处理器设备的支持能力

基于 .NET 的 Windows 传统,GC 需要配合 Windows 处理器组概念才能支持包含 64 块以上处理器的计算机。这一实现早在 5 到 10 年前的 .NET Framework 当中即已完成。但利用 .NET Core,我们最初选择了 Linux PAL 以实现类似的概念——虽然 Linux 当中不存在这一原生概念。此后,我们在 GC 当中放弃了这一概念,并将其转化为 Windows APL。

GC 现在公开了一个配置开关,即 GCHeapAffinitizeRanges,用于在包含 64 个以上处理器的计算机上指定相似的掩码。

GCLarge Page 支持

Large Pages  或者叫  Huge Pages 是一项功能,操作系统可利用其构建起大于原生页面大小(通常为 4K)的内存区域,从而提高调用这些大页面的应用程序的性能表现。

当发生虚拟到物理地址的转换时,系统会首先查询(通常为并行)被称为转换后备缓冲区(TLB)的高速缓存,以检查是否存在可用于所访问虚拟地址的可用物理翻译,以避免进行可能占用大量资源的页面移动操作。每个大页面翻译都需要使用 CPU 内部的一个翻译缓冲区。该缓冲区的大小通常要比本地页面大三个数量级;这能够极大提高转换缓冲区的效率,从而改善内存在频繁访问下的性能。在具有双层 TLB 的虚拟机当中,这一改进显得尤为重要。

GC 现在能够利用 GCLargePages 选项功能进行配置,从而选择在 Windows 上分配大页面。利用大页面能够减少 TLB 遗漏,进而在总体上提高应用程序的性能;但是,这项功能也有着自己的局限,应当认真考量。Bing.com 就采用了这项功能,并切实获得了性能提升。

.NET Core 版本 API

我们在.NET Core 3.0 当中对.NET Core 版本 API 做出改进,现在它们能够返回您所需要的版本信息。这些变更虽然在客观上带来了便利,但却存在技术破坏性,而且有可能影响到依赖现有版本 API 获取各类信息的应用程序。

现在,大家可以访问以下版本信息:复制代码

C:\git\testapps\versioninfo>dotnet run  **.NET Core info**  Environment.Version: 3.0.0  RuntimeInformation.FrameworkDescription: .NET Core 3.0.0  CoreCLR Build: 3.0.0  CoreCLR Hash: ac25be694a5385a6a1496db40de932df0689b742  CoreFX Build: 3.0.0  CoreFX Hash: 1bb52e6a3db7f3673a3825f3677b9f27b9af99aa     **Environment info**  Environment.OSVersion: Microsoft Windows NT 6.2.9200.0  RuntimeInformation.OSDescription: Microsoft Windows 10.0.18970  RuntimeInformation.OSArchitecture: X64  Environment.ProcessorCount: 8     

Event Pipe 改进Event Pipe 现在支持多个会话。这意味着大家可以使用 EventListener 在进程内使用事件,同时获得进程外 event pipe 客户端。

添加了新的 Perf Counters:

  • GC 中的时间百分比
  • Gen 0 堆大小
  • Gen 1 堆大小
  • Gen 2 堆大小
  • LOH 堆大小
  • 分配率
  • 加载组件数量
  • ThreadPool 线程数量
  • 锁定争用率
  • ThreadPool 工作条目队列
  • ThreadPool 已完成工作项比例

现在,大家可以使用同样的 Event Pipe 基础设施实现 Profiler 险别。

HTTP/2 支持

现在,我们在 HttpClinet 当中支持 HTTP/2。采用新协议是为了满足某些 API 的要求,例如 gRPC 与 Apple Push Notification Service。我们希望未来会有更多服务使用 HTTP/2 协议。此外,ASP.NET 也同样支持 HTTP/2。

备注:首选 HTTP 协议版本将通过 TLS/ALPN 协商,并仅在服务器选择使用 HTTP/2 时才加以使用。

分层编译

分层编译是 .NET Core 2.1 中的一项可选功能,该功能使得运行时能够更灵活地使用即时(JIT)编译器,从而在启动时获得更佳性能并最大程度提升吞吐量。.NET Core 3.0 在默认情况下即启用这一功能。去年,我们对该功能做出了一系列改进,包括针对各类工作负载(例如网站、PowerShell 以及 Windows 桌面应用程序)进行测试。性能确实迎来显著提升,因此我们决定将其作为默认选项。

IEEE 浮点改进

我们对浮点 API 进行了更新,以符合 IEEE 754-2008 修订版的要求。.NET Core 浮点项目的目标,在于公开所有“必要”运算,以确保其在行为层面符合 IEEE 提出的规范。

解析与格式化修复:

  • 正确解析并舍入任何长度的输入。
  • 正确解析并格式化负零。
  • 通过进行不区分大小写的检查,并在适用时允许使用可选前置 + 以正确解析无限与 NaN。

新的 Math API:

  • BitIncrement/BitDecrement ——对应于 nextUp 与 nextDown IEEE 运算。二者分别返回大于或者小于输入内容的最小浮点数。例如,Math.BitIncrement(0.0)将返回 double.Epsilon。
  • MaxMagnitude/MinMagnitude——对应于 maxNumMag 与 minNumMag IEEE 运算,二者分别返回大于或者小于输入内容的值。例如,Math.MaxMagnitude(2.0 -3.0) 将返回 -3.0。
  • ILogB ——对应于 logB IEEE 运算,该运算将返回一个整数值,且该值为输入参数的以 2 为底的整数对数。实际上与 floor(log2(x)) 相同,但舍入误差最小。
  • ScaleB——对应于采用整数值的 scaleB IEEE 运算,其相当于返回 x * pow(2,n) 的值,但舍入误差最小。
  • Log2——对应于 log2 IEEE 运算,返回以 2 为底的对数,且舍入误差最小。
  • FusedMultiplyAdd ——对应于 fma IEEE 运算,负责执行积和熔加运算。具体而言,其通过一次运算完成(x * y)+ z 运算,同时使舍入误差最小。以 FusedMultiplyAdd (1e308, 2.0, -1e308) 为例,其返回值为 1e308。常规(1e308 * 2.0)-1e308 将返回 double.PositiveInfinity。
  • CopySign ——对应于 copySign IEEE 运算,其返回 x 的值,但同时带有 y 符号。

.NET 平台相关改进

我们添加了一些新的 API,用以访问某些面向性能的 CPU 指令,包括 SIMD 或 Bit Manipulation 指令集。这些指令能够在某些场景下实现巨大的性能提升,例如更高效地并行处理数据。除了公开这些可供应用程序使用的 API 之外,我们还开始利用相关指令对.NET 库进行提速。

以下 CoreCLR PRs 通过实现或者使用的方式展示了一部分内部函数:

现可在 Linux 上支持 TLS 1.3 与 OpenSSL 1.1.1

.NET Core 现在能够利用 OpenSSL 1.1.1 中的 TLS 1.3 支持能力。TLS 1.3 能够为 OpenSSL 团队提供多项优势:

  • 通过减少客户端与服务器间的往返次数缩短连接时长。
  • 通过消除各种过时及不安全的加密算法、同时加密更多连接握手,以提高安全性。

.NET Core 3. 能够利用 OpenSSL 1.1.1、OpenSSL 1.1.0 以及 OpenSSL 1.0.2(以及您能够在 Linux 系统上找到任何最佳版本)。如果客户端与服务器均支持 TLS 1.3,且 OpenSSL 1.1.1 可用,则 SslStream 与 HttpClinet 类型将在使用 SslProtocols.None 时选择 TLS 1.3(系统默认协议)。

.NET Core 后续还将在 Windows 与 MacOS 上支持 TLS 1.3——预计以自动化方式实现,敬请期待。

加密

我们增加了对 AES-GCM 以及 AES-CCM 加密机制的支持,这些加密算法将通过 System.Security.Cryptography.AesGcm 与 System.Security.Cryptography.AesCcm 实现。这些算法皆属于带关联数据的加密认证(AEAD)算法,同时也是首批被添加至 .NET Core 当中的验证加密(AE)算法。

.NET Core 3.0 现在支持通过标准格式导入及导出非对称公钥与私钥,且无需使用 X.509 证书。

所有密钥类型(RSA、DSA、ECDsa、ECDiffieHellman)都支持 X.509 SubjectPublicKeyInfo 格式的公钥,以及 PKCS#8 PrivateKeyInfo 与 PKCS#8 EncryptedPrivateKeyInfo 格式的私钥。此外,RSA 还支持 PKCS#1 RSAPublicKey 与 PKCS#1 RSAPrivateKey。所有导出方法皆产生 DER 编码的二进制数据,而导入方法也基本相同;如果某一密钥以文本友好的 PEM 格式存储,则调用程序将需要先对内容进行 base64 解码,而后才能进行方法导入。

PKCS#8 文件可通过 System.Security.Cryptography.Pkcs.Pkcs8PrivateKeyInfo 类进行检查。

PFX/PKCS#12 文件可分别通过 System.Security.Cryptography.Pkcs.Pkcs12Info 与 System.Security.Cryptography.Pkcs.Pkcs12Builder 进行检查与修改。

支持新的日本年号(令和)

2019 年 5 月 1 日,日本开始使用新的年号——令和。因此,支持日语日历的软件(例如 .NET Core)必须进行更新以支持这一新年号。.NET Core 与 .NET Framework 已经更新,现在可以正确处理新年号下的日文日期格式与解析结果。

.NET 依赖于操作系统或其他更新以正确处理令和日期。如果您或者您的客户使用 Windows 系统,请下载 Windows 版本的最新更新。如果您运行的是 MacOS 或者 Linux,请下载并安装支持日本新年号的 ICU 64.2 版本。

Assembly Load Context 改进

关于 AssemblyLoadContext 的改进:

  • 实现上下文命名
  • 添加 ALC 枚举功能
  • 对 ALC 内程序集进行枚举的功能
  • 实现类型具体化——旨在简化实例化(简单场景不需要自定义类型)

通过将 AssemblyDependencyResolyer 与自定义 AssemblyLoadContext 加以结合,应用程序将能够在加载插件的过程中,从各个正确位置加载该插件所需要的依赖项,且某一插件的依赖项不会与其他插件的依赖项发生冲突。

Assembly 可卸载性

Assembly 的可制裁性是 AssemblyLoadContext 中提供的一项新功能。从 API 的角度来看,这项新功能具有极高的透明度,且面向部分新 API 公开。其允许开发人员对加载程序的上下文进行卸载,从而释放原本被实例化类型、静态字段以及程序集本身所占用的内存。应用程序可以通过这种机制实现程序集的永久加载与卸载,且不会引发内存泄漏。

我们希望这项新功能被用于以下场景:

  • 需要动态插件加载与卸载的插件使用场景。
  • 对代码的动态编译、运行以及刷新。适用于网站、脚本引擎等。
  • 加载程序集以进行自我检查(例如 ReflectionOnlyLoad),但在大多数情况下 MetadataLoadContext 仍然是更好的选择。

利用 MetadataLoadContext 读取 Assembly 元数据

我们添加了 MetadataLoadContext,希望在不影响调用程序域的前提下读取 assembly 元数据。各 assembly 这以数据的形式读取,其中包括与当前运行时环境不同的各类架构及平台上的 assembly。MetadataLoadContext 与 ReflectionOnlyLoad 存在一定交集,但后者仅适用于 .NET Framework。

MetdataLoadContext 目前通过 System.Reflection.MetadataLoadContext 软件包发布,其属于 .NET Standard 2.0 软件包。

MetadataLoadContext 的适用场景包括设计时功能、构建时工具与运行时点亮功能等需要将一组 assembly 作为数据进行检查,并在执行检查后释放所有文件锁与内存的场景。

Native Hosting 示例

.NET Core 团队还发布了一个 Native Hosting 示例,其中展示了在本机应用程序当中托管 .NET Core 的相关最佳实践。

作为 .NET Core 3.0 中的组成部分,我们现在面向 .NET Core 本地主机公开了这一原本只通过官方 .NET Core 主机为 .NET Core 托管应用程序提供的功能。此项功能主要与 assembly 加载相关,利用这项功能可帮助大家更轻松地构建能够利用 .NET Core 完整功能集的本地主机。

其他 API 改进

我们曾在 .NET Core 2.1 版本当中对 Span、Memory以及其他相关类型做出优化。现在,span construction、切片、解析以及格式化等常规操作的执行效果将有所提升。此外,String 等类型也得到了明显改进,能够在与 Dictionary<TKey,TValue> 及其他集合共同充当键时拥有更好的执行效率。所有改进开箱即用,无需对您的代码做出任何变更。

下面来看其它最新改进:

  • Brotli 内置 HttpClient 支持能力
  • ThreadPool.UnsafeQueueWorkItem(IThreadPoolWorkItem)
  • Unsafe.Unbox
  • CancellationToken.Unregister
  • 复杂算术运算符
  • Socket APIs for TCP 仍然有效
  • StringBuilder.GetChunks
  • IPEndPoint 解析
  • RandomNumberGenerator.GetInt32
  • System.Buffers.SequenceReader

默认情况下,应用程序现在具有原生可执行文件

.NET Core 应用程序现在拥有自己的原生可执行文件。对于依赖框架的应用程序而言,这是一项前所未有的新功能。在此之前,只有独立应用程序具有可执行文件。这些新的可执行文件,将在效果上与此前的原生可执行文件保持一致:

  • 您可以双击可执行文件以启动对应应用程序。
  • 您可以在 Windows 上使用 myapp.exe,或者在 Linux 及 MacOS 上使用./myapp 通过命令提示符启动应用程序。

作为 build 的组成部分,新生成的可执行文件将与您的操作系统以及 CPU 相匹配。例如,如果您使用的是 Linux x64 计算机,那么可执行文件将仅可在该类型的计算机上运行——无法在 Windows 以及 Linux ARM 计算机上运行。这是因为可执行文件使用本机代码(例如 C++)。如果要定位为其他机器类型,则需要在发布时使用对应的运行时参数。如果愿意,您也可以继续使用 dotnet 命令启动应用程序,而不使用原生可执行文件。

利用 ReadyToRun 镜像优化 .NET Core 应用

通过将应用程序 assemblies 编译为 ReadyToRun(R2R)格式,可以改进 .NET Core 应用程序的启动速度。R2R 是一种提前(AOT)编译格式,在 .NET Core 3.0 版本中以可选功能的形式提供。

R2R 二进制文件能够减少应用程序加载时 JIT 需要完成的工作量,从而提高启动性能。二进制文件当中包含与 JIT 产生的代码相似的本机代码,能够通过分担 JIT 的负载压力带来更理想的启动性能。R2R 二进制文件体积较大,因为其中既包含中间语言(IL)代码(某些情况下仍然需要此代码),同时包含同一代码的本机版本,用以改善启动速度。

要启用 R2R 编译:

  • 将 PublishReadyToRun 属性设置为 true。
  • 使用 RuntimeIdentifie 显式参数发布。

备注:在编译应用程序 assemblies 时,生成的本机代码将特定于当前平台与架构(因此,大家在发布时必须指定有效的 RuntimeIdentifier)。

以下为具体示例:复制代码

<Project Sdk="Microsoft.NET.Sdk">    <PropertyGroup>      <OutputType>Exe</OutputType>      <TargetFramework>netcoreapp3.0</TargetFramework>      <PublishReadyToRun>true</PublishReadyToRun>    </PropertyGroup>  </Project>    

使用以下命令进行发布:复制代码

dotnet publish -r win-x64 -c Release      

备注:RuntimeIdentifier 可设置为其他操作系统或者芯片类型,亦可在项目文件内进行设置。

Assembly Linking

.NET core 3.0 SDK 附带一款工具,可通过分析 IL 与修剪未使用的 assemblies 以降低应用程序的体积。这是 .NET Core 3.0 中的另一项发行时可选功能。

在 .NET Core 当中,我们可以随时发布包含代码运行所需的一切元素的自包含应用程序,而无需在部署目标上安装 .NET。在某些情况下,该应用仅需要框架中的一小部分即可运行,因此修剪掉不必要的框架部分能够有效降低应用程序体积。
我们利用 IL linker 对应用程序的 IL 进行扫描,从而检测出哪些代码确有必要,而后对未使用的框架库进行修剪。这能够显著降低某些应用程序的体积。一般来讲,小型工具控制台类应用程序的瘦身效果最为明显,因为其通常使用框架中的较小子集,且调整难度更低。

要使用 linker 工具:

  • 将 PublishTrimmed 的属性设置为 true。
  • 将 RuntimeIdentifier 作为显式参数进行发布。

以下为相关示例:复制代码

<Project Sdk="Microsoft.NET.Sdk">    <PropertyGroup>      <OutputType>Exe</OutputType>      <TargetFramework>netcoreapp3.0</TargetFramework>      <PublishTrimmed>true</PublishTrimmed>    </PropertyGroup>  </Project>      

使用以下命令进行发布:复制代码

 dotnet publish -r win-x64 -c Release     

备注:RuntimeIdentifier 可设置为其他操作系统或者芯片类型,亦可在项目文件内进行设置。

根据应用程序代码的实际调用情况,所发布的输出结果将包含框架库中的一个子集。对于最简单的 helloworld 应用,linker 工具能够将应用程序体积缩小 68 MB 到 28 MB。

在修剪之后,需要使用反射或者相关动态功能的应用程序或者框架(包括 ASP .NET Core 以及 WPF)通常无法正常执行,这是因为 linker 无法识别这种动态行为,因此一般不能确定反射操作在运行时中需要的框架类型。要修剪这类应用程序,大家需要告知 linker 相关代码当中所依赖的各类工具包或框架以及各种相关信息类型。请确保您在修剪之后对应用程序进行全面测试,我们也正在 .NET 5 当中对这一体验做出改进。

关于 IL Linker 的更多细节信息,请参阅说明文档或者访问 mono/linker 库。

备注:在 .NET Core 之前的版本当中,ILLink Tasks 以外部 NuGet 软件包的形式提供,其中包含多种相同功能。但相关软件包现已不再受到支持,请更新至 .NET Core 3.0 SDK 以获取我们为您准备的全新体验.

Linker 与 R2R 编译器可同时作用于一款应用程序。一般来讲,Linker 能够使您的应用程序体积更小,而 R2R 则能够再次恢复其体积,并在性能上带来显著提升。大家可以在各种配置当中进行测试,以了解各个选项带来的具体影响。

发布单文件可执行文件

现在,大家可以利用 dotnet publish 命令发布单文件可执行文件了。这种形式的单一 EXE 实际上是一个自解压可执行文件,其中以资源形式包含所有依赖项(包括本地依赖项)。在启动时,它会将所有依赖项复制到某个临时目录,通过该目录进行加载。依赖项只需要解压一次,后续启动将速度极快,不再存在任何性能损失。

大家可以通过在项目文件中添加 PublishSingleFile 属性,或者在命令行中添加新开关的方式启用这一发布选项。

要生成独立的单 EXE 应用程序,以下示例为 64 位 Windows 系统下的命令:复制代码

dotnet publish -r win10-x64 /p:PublishSingleFile=true    

备注:RuntimeIdentifier 可设置为其他操作系统或者芯片类型,亦可在项目文件内进行设置。

若需了解更多细节信息,请参阅单文件捆绑器说明文档。

Assembly 修剪器、提前编译(通过 crossgen)以及单文件捆绑功能皆为 .NET Core 3.0 中提供的全新选项,大家可以根据需求独立使用或者以组合方式使用。
相信一部分用户可能喜爱通过提前编译器提供的单 exe 发布选项,而非我们在.NET Core 3.0 当中提供的自解压可执行文件方法。.NET 5 发行版当中将正式提供提前编译器方法。

dotnet build 现在可复制依赖项

dotnet build 现在能够在 build 操作过程当中,将应用程序中的 NuGet 依赖项从 NuGet 缓存复制到 build 输出文件夹内。在此版本之前,各依赖项仅能作为 dotnet publish 中的一部分进行复制。此次变更使您能够利用 xcopy 将 build 输出至不同计算机当中。

除此之外,您还需要单独发布 linking 以及 razor page publishing 等操作。

.NET Core 工具:本地安装

.NET Core 工具也经过更新以支持本地安装。这些工具比 .NET Core 2.1 版本中的原有全局工具更强大。

本地安装具有以下特性:

  • 限制工具的使用范围。
  • 始终使用该工具的某一特定版本,该版本可能不同于全局安装工具或者其它本地安装工具。具体基于本地工具 manifest 文件中定义的版本。
  • 可使用 dotnet 命令启动,例如 dotnet mytool。

备注:请参阅本地工具早期预览版说明文档以了解更多细节信息。

.NET Core SDK 安装程序现在将就地升级

Windows 版本的 .NET Core SDK MSI 安装程序将迎来就地升级。这将减少开发者计算机及生产计算机上所需安装的 SDK 数量。

此项升级策略将专门针对 .NET Core SDK 功能范围。各功能范围在版本号中以最后三位数字中的百位体现,例如 3.0.101 与 3.0.201 代表两个不同的版本功能范围,而 3.0.101 与 3.0.199 则处于同一版本功能范围。

这意味着当 .NET Core SDK 3.0.101 正式发布并安装完毕后,.NET Core SDK 3.0.100 将被从计算机中删除(如果存在)。当 .NET Core SDK 3.0.200 可用并安装在同一台计算机上时,.NET Core SDK 3.0.101 则不会被删除。在这种情况下,系统仍将默认使用 .NET Core SDK 3.0.200,但如果将其配置为通过 global.json 起效,则系统仍然使用 .NET Core SDK 3.0.101(或者更高版本的.1xx 版本)。

这种方法与 global.json 的行为保持一致,允许在各补丁版本之间进行前滚 ,但无法跨越 SDK 功能范围。因此,通过 SDK 安装程序并进行升级,可确保应用程序不会因缺少 SDK 而发生错误。对于已经安装了 Visual Studio 必要 SDK 的用户,其功能范围也将与 Visual Studio 安装情况保持一致。

若需了解更多细节信息,请参阅:

.NET Core SDK 体积改进

在 .NET Core 3.0 当中,.NET Core SDK 的体积明显更小。主要原因在于,我们转而采用出于各种目的(引用 assembiles、框架、模板等)的专用“内置包”,从而改变了我们构建 SDK 的方式。在以往的版本(包括 .NET Core 2.2)当中,我们一直利用 NuGet 软件包构建 SDK,但其中包含大量并不需要的构件,且对存储空间造成严重浪费。

.NET Core 3.0 SDK 体积(括号中为体积变量)

操作系统安装程序体积(变化量)磁盘占用体积(变化量)
Windows164MB (-440KB; 0%)441MB (-968MB; -68.7%)
Linux115MB (-55MB; -32%)332MB (-1068MB; -76.2%)
macOS118MB (-51MB; -30%)337MB (-1063MB; -75.9%)

Linux 与 MacOS 版本的体积变化最为显著,相比之下 Windows 版本的改进较小,这是因为我们在 .NET Core 3.0 当中添加了 WPF 与 Windows Forms。令人惊讶的是,尽管新增了 WPF 与 Windows Forms,安装程序的体积仍然比原先小了一点。
大家也可以在 .NET Core SDK Docker 镜像当中感受到这一瘦身效果(仅限于 x64 Debian 与 Alpine)。

发行版2.2 体积3.0 体积
Debian1.74GB706MB
Alpine1.48GB422MB

大家可以在 .NET Core 3.0 SDK 体积改进文档中了解我们如何计算这些文件的大小。这里提供详细的说明,您可以遵循同样的方法在自己的环境中运行测试。

Docker 发布更新

微软各团队目前都在面向微软容器注册表(MCR)发布新的容器镜像。这一变化主要有两大原因:

  • 将微软提供的容器镜像协同至多个注册表,包括 Docker Hub 与 Red Hat。
  • 利用微软 Azure 作为全球 CDN 以交付微软提供的容器镜像。

在.NET 团队,我们目前将所有 .NET Core 镜像发布至 MCR。我们在 Docker Hub 上维护有自己的主页,并打算长期保持运营。在另一方面,MCR 并不提供类似的页面,但可以通过 Docker Hub 等公共注册表为用户提供与镜像有关的信息。
Microsoft.dotnet 以及 microsoft.dotnet-nightly 等旧有代码库链接现在都已经指向新的地址。不过旧有位置中的各镜像仍将存在,不会被删除。

在支持生命周期之内,我们将继续为 .NET Core 各个版本的代码库提供浮动标记服务。例如,2.1-sdk、2.2-runtime 以及 lastest 就属于此类浮动标记。像 2.1.2-sdk 这类包含三个数字的标签将不再提供,我们已经在版本号内加以体现。接下来,我们将仅通过 MCR 支持 .NET Core 3.0 镜像。

例如,现在用于提取 3.0 SDK 镜像的正确标签字符串应该为:复制代码

mcr.microsoft.com/dotnet/core/sdk:3.0      

新的 MCR 字符串将由 docker pull 以及 Dockerfile FROM 语句共同使用。

感兴趣的朋友可以参阅微软容器注册表中的现有 .NET Core 镜像以了解更多细节信息。

SDK Docker 镜像中包含 PowerShell Core

应社区的要求,PowerShell Core 已经被添加至 .NET Core SDK Docker 容器镜像当中。PowerShell Core 是一套跨平台(Windows、Linux 以及 MacOS)自动化与配置工具 / 框架,能够与大家的现有工具良好协作,并针对结构化数据(例如 JSON)CSV、XML 等)、REST API 以及对象模型进行了优化。其中包含一个命令行 shell、一种相关脚本语言以及一套用于处理 cmdlets 的框架。

现在,大家可以通过以下 Docker 命令将 PowerShell Core 视为 .NET Core SDK 容器镜像中的一部分:复制代码

docker run --rm mcr.microsoft.com/dotnet/core/sdk:3.0 pwsh -c Write-Host "Hello Powershell"     

目前只有两种将 PowerShell 引入 .NET Core SDK 容器镜像的方法,具体如下:

  • 面向任意操作系统利用 PowerShell 语法编写 .NET Core 应用程序 Dockerfiles。
  • 编写能够轻松实现容器化的 .NET Core 应用程序 / 库构建逻辑。

以下为容器化 build(存储卷挂载)的 PowerShell 启动语法示例:复制代码

docker run -it -v c:\myrepo:/myrepo -w /myrepo mcr.microsoft.com/dotnet/core/sdk:3.0 pwsh build.ps1 docker run -it -v c:\myrepo:/myrepo -w /myrepo mcr.microsoft.com/dotnet/core/sdk:3.0 ./build.ps1

为了让第二条示例命令正常起效,我们需要在 Linux 上确保.ps1 文件具有以下格式,并需要利用 Unix(LF)而非 Windows(CRLF)作为行尾以执行格式化:复制代码

#!/usr/bin/env pwsh  Write-Host "test"     

备注:PowerShell Core 目前已经作为.NET Core 3.0 SDK 容器镜像的一部分进行发布,而不再归属于 .NET Core 3.0 SDK。

红帽软件支持

2015 年 4 月,我们宣布 .NET Core 将全面登陆 Red Hat Enterprise Linux。通过与红帽方面的出色工程合作,.NET Core 1.0 于 2016 年 6 月以组件的形式出现在红帽系列软件当中。另外,在与红帽工程师们的持续交流当中,我们也了解到 Linux 社区在软件发布方面的反馈与意见。

过去四年以来,红帽公司先后发布了多次面向 .NET Core 的更新与大版本升级,包括 2.1 与 2.2 版本。在 .NET Core 2.2 版本当中,红帽公司将其 .NET Core 产品扩展至包括 OpenShift 在内的多个平台。而随着 RHEL 8 的发布,我们也高兴地看到微软 .NET Core 2.1 以及即将推出的 3.0 版本都被纳入红帽 Application Streams 当中。

总结

.NET Core 3.0 是 .NET Core 发展历程中的又一重要新版本,其中带来了大量改进。我们建议大家尽快着手采用新的 .NET Core 3.0。新版本通过多种方式对 .NET Core 做出巨大改进,例如显著降低了 SDK 的体积、极大提升对于关键场景(例如容器以及 Windows 桌面应用程序)的支持效果。受到篇幅所限,这里无法一一列出其它小的改进,但相信随着时间的推移,大家一定能够从这些细节增强中受益。

最后,希望大家能够在接下来的使用当中与我们分享体验与感受。希望开发者喜欢 .NET Core 3.0,我们也热切希望根据大家的喜好对产品进行打磨。

未经允许不得转载:PHP100中文网 - 中国第一档PHP资源分享门户 » .NET Core 3.0 正式公布:新特性详细解读

赞 (0) 打赏

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏