Liam W
封面

.NET 6 RC2 版本发布

作者
王亮·发表于 2 年前

原文:https://bit.ly/3FS9xm7
作者:Richard
日期:2021-10-12
翻译:精致码农-王亮
说明:文中有大量的超链接,这些链接在公众号文章中被自动剔除,一部分包含超链接列表的小段落被我删减了,如果你对此感兴趣,请参考阅读原文。

我们很高兴发布 .NET 6 候选版(RC) 2。这是两个“上线”候选版本中的第二个版本,可用于生产环境。在过去的几个月里,团队一直专注于质量改进。该版本有很多新功能,只有在接近尾声时才会完全整合。团队目前正在检验端到端的工作流程,以找到设计意图和技术现实还不完全匹配的地方。

我们从用户那里听说,将生产站点升级到 .NET 6 RC1 既“平滑”又“令人兴奋”(性能的显著提高)。我们相信,RC2 将继续保持这种令人兴奋的结果的趋势。

你可以下载 Linux、macOS 和 Windows 的 .NET 6 候选版 2[1]

  1. 安装程序和二进制文件[1]
  2. 容器镜像[2]
  3. Linux 软件包[3]
  4. 发布说明[4]
  5. API 差异[5]
  6. 已知问题[6]
  7. GitHub 问题跟踪[7]

请参阅 .NET MAUI 和 ASP.NET Core 的帖子,了解更多关于客户端和 Web 应用场景的新内容。

.NET Conf 是一个免费的、为期三天的虚拟开发者活动,庆祝 .NET 的发布。今年,.NET 6 将于 11 月 9 日至 11 日在 .NET Conf 上发布。我们鼓励你关注这个日期。Visual Studio 2022 预览版今天也将发布,他们也宣布了 11 月 8 日的正式发布活动。你可以在 Visual Studio 博客上阅读所有这些信息。

我们正处于这个周期的有趣部分,我们在生产中支持新版本。如果你需要关于如何处理问题的指导,你可以联系我们 dotnet@microsoft.com。一批企业已经伸出援手,现在已经投入生产。一些微软团队已经在 RC1 上投入生产,我们希望更多的团队在 RC2 上投入生产,这包括 .NET 官方网站。

.NET 6 RC2 已经过测试,支持 Visual Studio 2022 Preview 5,也是今天发布的。.NET 6 将被 Visual Studio 2022 支持,而不是 Visual Studio 2019。同样,它将被 MSBuild 17.x 而不是 16.x 支持。如果你想使用.NET 6,你将需要升级到 Visual Studio 2022。

Visual Studio 2022 for Mac 目前与 .NET 6 RC2 不兼容,我们正在解决这个问题。

请查看新的对话帖子[8],了解工程师之间关于最新的 .NET 功能的深入讨论。

.NET 6 RC1 的文章专注于基础功能,其中许多功能要到 .NET 7 才会完全实现。这篇文章的重点是 C# 10 和对模板的相关改进。它还包括对 macOS 和 Windows Arm64 的更新(其中包括了一些突破性的变化)。让我们来看看。

C# 10

C# 10 是 .NET 6 版本的一个重要组成部分。在大多数情况下,C# 10 是现有概念和能力的进一步发展,如 Record 和模式。它还包括一些功能 – global usingfile-scoped 命名空间–帮助你简化代码,减少编写模板。这些具体的改进是本文后面讨论的模板变化的基础。你可以在 .NET 6 的例子中看到本节中使用的例子[9]。我将在最后一篇 .NET 6 的文章中添加更多的例子。

Record 结构体

C# 10 增加了对 Record 结构的支持。这个新功能类似于 C# 9(基于类的)Record,但有一些关键的区别。在大多数情况下,Record 结构是为了完整性而添加的,以便结构可以享受与类相同的 Record 优点。然而,该团队并没有简单地将 Record 结构化,而是决定将结构化 Record 与 ValueTuple 一样与类 Record 保持一致。作为这种设计方法的结果,Record 的结构属性默认是可变的,而 Record 的类属性是不可变的。然而,您可以声明一个 readonly record struct,它是不可变的,并与 record class 的语义相匹配。

在高层次上,Record 结构并不取代 Record 类,我们也不鼓励将 Record 类迁移到 Record 结构上。使用类和结构的指南同样适用于 Record 类和 Record 结构。换句话说,在选择使用 Record 之前,应该在类和结构之间做出选择。

让我们来看看 Record 结构与 Record 类有什么不同:

Battery battery = new("CR2032", 0.235, 100);

WriteLine(battery);

while (battery.RemainingCapacityPercentage > 0)
{
    battery.RemainingCapacityPercentage--;
}

WriteLine(battery);

public record struct Battery(string Model, double TotalCapacityAmpHours, int RemainingCapacityPercentage);

这个 Record 结构的例子产生了以下输出:

Battery { Model = CR2032, TotalCapacityAmpHours = 0.235, RemainingCapacityPercentage = 100 }
Battery { Model = CR2032, TotalCapacityAmpHours = 0.235, RemainingCapacityPercentage = 0 }

如前所述,最明显的区别是,Record 结构的属性默认是可变的。这就是除了是结构和 record struct 语法之外的关键区别。我还用一个 readonly record struct 重写了这个例子,如下所示:

Battery battery = new("CR2032", 0.235, 100);

WriteLine(battery);

while (battery.RemainingCapacityPercentage > 0)
{
    Battery updatedBattery = battery with {RemainingCapacityPercentage = battery.RemainingCapacityPercentage - 1};
    battery = updatedBattery;
}

WriteLine(battery);

public readonly record struct Battery(string Model, double TotalCapacityAmpHours, int RemainingCapacityPercentage);

你会看到它与我为 C# 9 发布的(类)Record 例子[10]几乎完全相同。

让我们回顾一下 C# 9 的 Record。它们为定义类似结构的面向数据的类提供了一种简洁的语法。它们偏向于不可变性,同时为不可变性友好的复制提供了一种简明的语法–with 表达式。我们用类来实现一个旨在类似结构的功能,这可能让人们感到惊讶。大多数情况下,开发者使用类而不是结构,这是因为通过引用传递而非值语义。类在大多数情况下是最好的选择,而且更容易使用。

结构 Record 与类 Record 非常相似:

  • 它们使用相同的语法(定义中的 structclass 除外)。
  • 它们能够自定义成员定义(C# 10 中的新内容),使用字段而不是(默认的)属性成员。
  • 它们能够自定义成员行为,使用 init 或可变属性。
  • 它们支持表达式。事实上,从 C# 10 开始,所有结构类型都支持表达式。

结构 Record 与类 Record 的不同:

  • Record 结构是用 record structreadonly record struct 定义的。
  • Record 类是用 recordrecord class 来定义的。
  • Record 结构的属性默认是可变的(get/set)。
  • Record 类的属性默认是不可变的(get/init)。

Record 结构和 Record 类之间不对称的(不)可变性行为可能会让一些读者感到惊讶,甚至厌恶。我试着解释一下这个设计背后的想法。由于按值传递的语义,结构体并不像类那样从不可变性中获益。例如,如果一个结构是字典中的一个键,那么这个(结构的副本)键是不可变的,查找将通过等价来进行。同时,设计团队发现 ValueTuple 可以被视为匿名的 Record 结构,Record 结构可以被视为从 ValueTuple 发展而来的。这只有在 Record 结构拥抱可变性和支持字段的情况下才行得通,而这正是设计团队决定要做的。此外,拥抱可变性也反映了结构体和类的不同。

如果你喜欢 Record 结构的不可变行为,你可以通过添加 readonly 关键字来拥有它。

从总体上看,结构的关键功能是共同的:

  • 判断相等对所有结构都是一样的,由运行时提供。
  • 所有结构都可以使用 with 表达式来创建非变体副本,这是 C# 10 的新功能。

全局引用

global using 使你能够指定一个你希望在所有源文件中都可用的命名空间,就像它在每个文件中被声明一样。它也可以与 using static 和别名一起使用。这个特性使用一组通用的 using 声明省去许多不需要的 using 行。这与平台命名空间最为相关,但也可以用于任何命名空间。

下面的语法可以用于各种 using 形式:

  • global using System;
  • global using static System.Console;
  • global using E = System.Environment;

这些声明只需要在你的编译中声明一次就可以在整个编译中生效。有四种模式来声明 global using

  • Program.cs 中,将你的根 using 语句升级为全局 using,使其成为整个程序的全局。
  • GlobalUsings.cs 文件(或一些类似的名字)中,集中管理你所有的 global using 语句。
  • 在你的项目文件中,用下面的语法。
  • 在你的项目文件中,用下面的语法启用默认的平台 using 语句(针对你的应用程序所依赖的 MSBuild SDK)。

可以在 <ItemGroup> 中使用以下 MSBuild 语法来代替 .cs 文件:

  • <Using Include="System"/>
  • <Using Include="System.Console" Static="True"/>
  • <Using Include="System.Environment" Alias="E"/>

你可以启用平台在 <PropertyGroup> 中定义的隐式 using 语句。

  • <ImplicitUsings>enable</ImplicitUsings>

隐式 using 因 MSBuild SDK 而异[11]Microsoft.NET.Sdk 定义了一个基本集合,而其他 SDK 定义了额外的集合。

如果你使用 MSBuild 功能,你可以在 obj 目录下生成的文件中看到有效的结果,如下所示:

PS C:\Users\rich\app> type .\app.csproj | findstr Using
    <ImplicitUsings>enable</ImplicitUsings>
    <Using Include="System.Console" Static="True"/>
    <Using Include="System.Environment" Alias="E"/>
PS C:\Users\rich\app> type .\obj\Debug\net6.0\app.GlobalUsings.g.cs
// <auto-generated/>
global using global::System;
global using global::System.Collections.Generic;
global using global::System.IO;
global using global::System.Linq;
global using global::System.Net.Http;
global using global::System.Threading;
global using global::System.Threading.Tasks;
global using E = global::System.Environment;
global using static global::System.Console;

你可以看到,Microsoft.NET.Sdk.GlobalUsings.g.cs 文件中添加了几个 global using 语句。请注意,global:: 操作符不是必须的,可以安全地忽略它。

文件范围命名空间

文件范围(File-scoped)的命名空间声明是 C# 10 的另一个特性,旨在减少缩进和行数。

这一特性的语法如下:

namespace Foo;

它是传统的三行语法的一种替代:

namespace Foo
{
}

三行语法可以嵌套,单行语法不支持嵌套。每个文件只能有一个文件范围的声明。它必须在文件中定义的所有类型之前,与三行语法很相似。

命名空间与顶层语句不兼容。顶层语句存在于顶层命名空间中。这也意味着,如果你使用 partial class 的语法给 Program 类添加额外的方法,那么分部 Program 类也需要在顶级命名空间中。

常量插值字符串

插值字符串现在可以被分配给 const 常量。插值字符串的使用和阅读都很直观,而且应该在任何地方都可以使用。它们现在可以和 const 一起使用,前提是占位符的值也是常量。

下面的例子演示了一个可以的使用情况:

const string Bar = "Bar";
const string DoubleBar = 
quot;
{Bar}_{Bar}"
; WriteLine(DoubleBar);

对插值字符串进行了许多其他改进,这些改进在《C# 10 和.NET 6 中的字符串插值》[12]中有所描述。

扩展属性模式

你现在可以在一个属性模式中引用嵌套的属性或字段。例如,以下模式现在是合法的:

{ Prop1.Prop2: pattern }

以前,你需要使用以下模式:

{ Prop1: { Prop2: pattern } }

你可以在下面的例子中看到这种更紧凑的模式,例如用 Reading.PM25

List<Status> statuses = new()
{
    new(Category.Normal, new(20, false, 20)),
    new(Category.Warning, new(20, false, 60)),
    new(Category.Danger, new(20, true, 60)),
    new(Category.Danger, new(100, false, 20))
};

foreach (Status status in statuses)
{
    string message = status switch
    {
        {Category: Category.Normal} => "Let the good times roll",
        {Category: Category.Warning, Reading.PM25: >50 and <100} => "Check the air filters",
        {Reading.PM25: >200 } => "There must be a fire somewhere. Don't go outside.",
        {Reading.SmokeDetected: true } => "We have a fire!",
        {Category: Category.Danger} => "Something is badly wrong",
        _ => "Unknown status"
    };

    Console.WriteLine(message);
}


record struct Reading(int Temperature, bool SmokeDetected, int PM25);
record struct Status(Category Category, Reading Reading);
enum Category
{
    Normal,
    Warning,
    Danger
}

.NET SDK:现代化 C# 项目模板

我们在 Preview 7 中对 .NET SDK 模板进行了现代化处理,使用了最新的 C# 功能和模式。对这些变化有很大的反馈,部分原因是构建开始失败。作为初始模板更新的一部分,我们为 .NET 6(net6.0)项目默认启用了隐式引用(又称 opt-out)。我们已经更新了 SDK,使所有的新功能都可以选择加入,对这一变化(在 RC1 中进行)的反馈是积极的。

也有反馈说一些人不喜欢新的简化的 Program.cs 文件,其中有顶层语句。作为回应,我们对顶层语句进行了改进,并继续将其用于模板。我们预计,大多数喜欢传统方法的开发者可以直接自己添加额外的仪式。

以下是新模板中使用的语言特性。

  • async Main
  • 顶层语句
  • 目标类型 new 表达式
  • 全局引用指令
  • 文件范围命名空间
  • 可归零的引用类型

我们创建了所有这些功能,因为我们认为它们比以前的替代方案更好。模板是引导新开发者和新应用使用最佳模式的最简单和最好的方法。C# 设计团队非常相信能够使用更少的行,更少的字符来指定一个特定的概念或操作,并减少不必要的重复。这就是这些新功能的大部分作用。Nullable 的不同之处在于,它能使代码更加可靠。每一个使用 nullable 的应用程序或库在生产中崩溃的可能性较小。软件太复杂了,人类的大脑无法看到编译器所能看到的错误。

这些功能的一个共同主题是,当你在代码编辑器中看你的代码时,它们会减少噪音,增加信号。更重要的方面现在应该显现出来。例如,如果你在一个给定的文件中看到一个 using 语句,它是一个需要超越隐式集的特殊语句。你应该能够将代码从一个文件复制/粘贴到另一个文件,而不需要 CTRL-. 来添加所需的命名空间(至少没有那么多)。如果你看到 nullable 警告或错误,你就知道你的代码可能在某些方面不正确。移除缩进也是有好处的。更多的代码将在编辑器的前十几列中可见,而不是在这些列中被空格字符填满。我们可以把这些功能看作是向你的眼睛提供更高密度的特殊性和意义。

控制台模板

让我们从控制台模板开始,它非常简约:

Program.cs

// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");

项目文件:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

</Project>

尽管控制台模板比它的 .NET 5 对应的模板要简约得多,但功能并没有减少。你还可以看到,ImplicitUsings 现在是一个可选择的功能,并在模板中被启用。

Web 模板

web 模板也简约了很多:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => "Hello World!");

app.Run();

webapi 模板更贴近典型的 ASP.NET Core 应用程序。你会很快注意到,Program.csStartup.cs 之间的分割已经被移除。Startup.cs 不再是必需的。

Program.cs:

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

webapi 模板的项目文件与 console 的类似。

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Swashbuckle.AspNetCore" Version="6.1.5" />
  </ItemGroup>

</Project>

你可以看到,文件范围命名空间在这个样本中被使用,在 WeatherForecast.cs 中:

namespace webapi;

public class WeatherForecast
{
    public DateTime Date { get; set; }
    public int TemperatureC { get; set; }
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
    public string? Summary { get; set; }
}

WeatherForecastController.cs 中使用了目标类型(Target-type)new 表达式:

private static readonly string[] Summaries = new[]
{
    "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};

mvc 模板中,你可以看到使用 nullable 注解和一个 expression-bodied 方法。

namespace webmvc.Models;

public class ErrorViewModel
{
    public string? RequestId { get; set; }

    public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
}

Windows Forms 模板

Windows Forms 模板也已更新。它包括了其他模板所涉及的大部分改进,但值得注意的是没有包括顶层语句。

namespace winforms;

static class Program
{
    /// <summary>
    ///  The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        ApplicationConfiguration.Initialize();
        Application.Run(new Form1());
    }
}

你可以看到单行命名空间语句的引用,以及由于启用了隐式引用,没有了任何平台级的 using 语句。

WPF 模板并没有作为该版本的一部分被更新。

隐式引用

现在我将向你展示这些功能的实际应用。让我们从隐式引用开始。当启用时,每个 Sdk 都会添加自己的隐式引用语句集。

正如前面所展示的,Microsoft.NET.Sdk 添加了几个 global using 语句。

如果这个功能被禁用,你会看到应用程序不再被编译,因为 System 命名空间(在这个例子中)不再被声明。

PS C:Usersrichapp> type .app.csproj | findstr Implicit
    <ImplicitUsings>disable</ImplicitUsings>
PS C:Usersrichapp> dotnet build
Microsoft (R) Build Engine version 17.0.0-preview-21501-01+bbcce1dff for .NET
Copyright (C) Microsoft Corporation. All rights reserved.

  Determining projects to restore...
  All projects are up-to-date for restore.
  You are using a preview version of .NET. See: https://aka.ms/dotnet-core-preview
C:UsersrichappProgram.cs(2,1): error CS0103: The name 'Console' does not exist in the current context [C:Usersrichappapp.csproj]

Build FAILED.

另一个选择是使用全局引用功能,它使你只使用你想要的命名空间,正如你在下面的例子中看到的:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>disable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <Using Include="System" />
  </ItemGroup>

</Project>

现在,编译代码:

PS C:Usersrichapp> type .app.csproj | findstr Using
    <ImplicitUsings>disable</ImplicitUsings>
  <Using Include="System" />
PS C:Usersrichapp> dotnet build
Microsoft (R) Build Engine version 17.0.0-preview-21501-01+bbcce1dff for .NET
Copyright (C) Microsoft Corporation. All rights reserved.

  Determining projects to restore...
  All projects are up-to-date for restore.
  You are using a preview version of .NET. See: https://aka.ms/dotnet-core-preview
  app -> C:UsersrichappbinDebugnet6.0app.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:00.80

这不是推荐的一般模式,但对于希望获得最大控制权的人来说是个不错的选择。在大多数情况下,我们希望开发者依靠 SDK 提供的隐式引用,并利用他们自己的代码或普遍使用的 NuGet 包中的显式全局引用来实现命名空间。

Nullable

我更新了 Program.cs 以展示可忽略的引用类型。该应用程序调用了一个 List<T> 方法,该方法返回一个 T,在这里是一个可空的字符串(string?

List<string> greetings = new()
{
    "Nice day, eh?"
};

string? hello = greetings.Find(x => x.EndsWith("!"));
string greeting = hello ?? "Hello world!";

Console.WriteLine(
quot;There are
{greeting.Length} characters in "
{greeting}"");

定义 hello 变量的那一行,如果不定义为 varstring? 或解决 List<T>.Find 返回 null 的情况,就无法编译。如果没有启用 nullable 功能,我可能会错过这个问题,这将导致我的代码以 NullReferenceException 异常崩溃。我在下一行用 ?? 处理这个问题,这是一个空值凝聚运算符。在大多数情况下,这两行会被合并成一行,正如你在下面的代码中看到的那样。我把它们分开(在这个假想的例子中),这样你就可以看到我使用 string?

string greeting = greetings.Find(x => x.EndsWith("!")) ?? "Hello world!";

在这个例子中,我把所有的东西都合并到了一行。现在我可以将变量声明为 string,因为如果返回空值就会被 ?? 后面的字符串所替代。本例中的 string? 是只有编译器才能看到的。

string[] args

我现在要快速解决大多数人对顶层语句提出的问题,包括 args 参数。args 参数在顶层语句中仍然可用。它只是不那么明显。下面是另一个演示使用它的程序:

string greeting = args.Length > 0 ? string.Join(" ", args) : "Hello World!";
WriteLine(
quot;There are
{greeting.Length} characters in "
{greeting}" in this {nameof(Program)}.");

它的输出如下:

PS C:\Users\rich\app> dotnet run
There are 12 characters in "Hello World!" in this Program.
PS C:\Users\rich\app> dotnet run Nice day, eh?
There are 13 characters in "Nice day, eh?" in this Program.

我在 Program.cs 中删除了 Console.WriteLine 中的 Console,方法是在我的项目文件中添加一个全局静态使用,如下所示:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <Using Include="System.Console" Static="True"/>
  </ItemGroup>

</Project>

定义常规方法

我们已经听到一些人的反馈,认为真实方法比局部函数更受欢迎,特别是对于 Program 类。你可以在顶层语句中使用两种模式。

我将程序保持不变,但将所有的功能切换到 Program 类中的静态方法,定义在一个分部类中。

Program.cs:

WriteLine(GetTheGreeting(args));

Program.Foo.cs:

public partial class Program
{
    public static string GetTheGreeting(string[] args)
    {
        string greeting = args.Length > 0 ? string.Join(" ", args) : "Hello World!";
        return 
quot;There are
{greeting.Length} characters in "
{greeting}" in this {nameof(Program)}."; } }

它产生的结果与前面的例子相同,Program.Foo.cs 的文件名是任意的。事实上,Program.cs 也是如此,你可以给这些文件起任何你想要的名字。编译器会理清楚的。

如前所述,使用顶层语句时,Program 类型必须在顶层命名空间中。

macOS 和 Windows Arm64 更新

我们有好消息,但也有一些突破性的变化要分享。支持 macOS 和 Windows arm64 的项目几乎已经完成。我们已经为这个项目工作了一年多,在需要的时候,我们得到了 macOS 和 Windows 团队的好心人的帮助。起初,我们认为这个项目只是为了支持 macOS 上的 Arm64,Rosetta 2 将涵盖 x64。很简单,只是关于 ISA,对吗?并非如此。随着我们的深入,很明显 x64 和 Arm64 的共存是更大的任务,重点是 CLI 和安装程序,包括一些他们需要达成一致的地方。通过 RC2,我们正在交付 .NET 的构建,我们提供了我们可以想象的最佳体验。

我将对我们在 macOS 和 Windows Arm64 机器上的进展情况进行快速总结:

  • .NET 6 RC2 通过将 Arm64 和 x64 版本安装到不同的位置,实现了 Arm64 + x64 的共存。到目前为止,Arm64 和 x64 版本相互覆盖,这导致了普遍的遗憾。
  • 你需要卸载所有的 .NET 构建,并从头开始(在 macOS 和 Windows Arm64 机器上)采用 .NET 6 RC2+。这包括 Arm64 和 x64,.NET 6 和 pre-.NET 6。
  • Pre .NET 6 的构建尚未准备好安装。
  • CLI 使您能够使用 Arm64 SDK 进行 Arm64 和 x64 开发(假设您安装了所需的 Arm64 和 x64 运行时)。反过来也是如此。
  • 我们希望人们只使用 Arm64 SDK,因为这将是一个更好的体验(原生架构性能,仅需维护一个 SDK)。我们将继续改进产品,使这种模式成为大多数开发者的简单选择。
  • 对于 SDK,我们将只支持 Arm64 上的 .NET 6+。早期的 SDK 构建将在 Arm64 上被阻止。
  • 对于运行时,我们将支持所有支持的版本,Arm64 和 x64。
  • .NET 6 RC2 为 Arm64 提供了大部分最终的 .NET 6 体验(包括 x64 模拟)。
  • 我们希望能更新 .NET Core 3.1 和 .NET 5 运行系统,以便与 .NET 6 RTM 保持一致(在时间和技术要求上),这仍然是待定的。
  • 用于 Windows Arm64 的 .NET 5 SDK 将随着 .NET 6 RTM 而提前停止支持。

我们也在考虑对 dotnet test 进行突破性修改,以统一我们用于架构定位的参数。

  • 【Breaking change】对于 dotnet test,将 -a 改为别名 -arch,而不是 -test-adapter-path
  • 【Breaking change】对于 dotnet test,将 -r 切换到别名 -runtime,而不是 -results-dir

该项目的一个重要部分是通过 Arm64 SDK 实现 x64 运行时的使用。你可以在下面的图片中看到这个演示。

最后

C# 10 在 C# 9 类似功能的基础上,在简单性和表现力方面有了很大的改进。在过去,人们可以合理地嘲笑 C# 需要如此多的仪式和面向对象概念的知识来编写一行代码。那些日子已经过去了,这些模板也反映了这一点。这些变化的部分动机是为了使 C# 对新的程序员和学校更具吸引力。这些简化的变化从根本上改变了你需要学习的内容,以便更快地开始使用并熟练使用 C#。在其新的默认形式下,它可以直接与其他类似从单一源文件开始的语言相媲美。

我们期待着看到其他项目通过顶层语句、全局引用、Record 和许多其他现代功能来简化他们的用户体验。在为开源项目和商业产品创造更简单的学习旅程方面有很多机会。

我们鼓励你使用 .NET 6 RC2。我们相信,你会发现这是一个可靠的版本。敬请关注 11 月 9 日的 .NET 大会,以参加 .NET 6 的发布。

感谢您成为一名 .NET 开发者!

文中链接:

[1]. https://dotnet.microsoft.com/download/dotnet/6.0
[2]. https://hub.docker.com/_/microsoft-dotnet
[3]. https://github.com/dotnet/core/blob/main/release-notes/6.0/install-linux.md
[4]. https://github.com/dotnet/core/blob/main/release-notes/6.0/README.md
[5]. https://github.com/dotnet/core/tree/main/release-notes/6.0/preview/api-diff/rc1
[6]. https://github.com/dotnet/core/blob/main/release-notes/6.0/known-issues.md
[7]. https://github.com/dotnet/core/issues/6795
[8]. https://devblogs.microsoft.com/dotnet/category/conversations/
[9]. https://gist.github.com/richlander/09a18038de1edd27468b7d5966e38df6
[10]. https://gist.github.com/richlander/bcbbcc9e0b541a06eb805d663ebf6334
[11]. https://docs.microsoft.com/dotnet/core/compatibility/sdk/6.0/implicit-namespaces-rc1#new-behavior
[12]. https://devblogs.microsoft.com/dotnet/string-interpolation-in-c-10-and-net-6/