官宣 .NET 6 RC (Release Candidate) 2

我们很高兴发布 .NET 6 RC(Release Candidate) 2。它是生产环境中支持的两个“go live”候选版本中的第二个。在过去的几个月里,团队一直专注于质量的改进。这个版本中有很多的新特性,但在接近尾声时我们才会把他们完全整合在一起。该团队目前正在验证端到端工作流,以找到设计意图和技术现实尚未完全匹配的地方。这导致团队里的维护人员不得不修改bug来达到这个目的。

我们从用户那里听说,将生产站点升级到.NET 6是既“无聊”(非事件)又“令人兴奋”(显著的性能改进)的。不出意外,我们相信RC2将继续保持这种趋势。

您可以下载适用于 Linux、macOS 和 Windows 的 .NET 6 Release Candidate 2。

安装程序和二进制文件

容器镜像

Linux包

发布说明

API diff

已知的问题

GitHub问题跟踪器

请参阅 .NET MAUI ASP.NET Core 帖子,了解有关客户端和 Web 应用程序场景新增功能的更多详细信息。

.NET 大会是一个免费的,为期三天的虚拟开发者活动,用来庆祝.NET的主要版本。今年,.NET 6 将于 11 月 9 日至 11 日在 .NET 大会上发布。我们期待您可以记下日期准时收听。Visual Studio 2022 预览版 5 也在今天发布,他们还宣布将于 11 月 8 日举行发布活动。您可以在 Visual Studio 博客上阅读所有相关内容。

我们正处于支持生产新版本的周期的这一有趣部分中。我们真诚地鼓励它。如果你需要有关如何处理的指导,可以通过[email protected]联系我们。许多企业已经开始接触,有些已经开始生产。我们还帮助 Microsoft 团队在 RC 版本上运行。一些 Microsoft 团队已经在 RC1 上投入生产,我们期待在 RC2 上有更多帮助。这包括 .NET 网站

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

支持Mac版本的Visual Studio 2022目前与.NET 6 RC2不兼容。我们正在解决这个问题。

查看新的对话帖子,就最新的 .NET 功能进行工程师对工程师的深入讨论。

.NET 6 RC1 帖子侧重于基础功能,其中许多功能要到 .NET 7 才能完全实现。本文重点介绍 C# 10 和模板的相关改进。它还包括 macOS 和 Windows Arm64 的更新(包括重大更改)。让我们来看看。

C#10

C# 10是 .NET 6 版本的重要组成部分。在很大程度上,C# 10是对现有概念和功能(如记录和模式)的进一步演变。它还包括一些特性——全局使用和文件作用域的命名空间——可以帮助您简化代码并编写更少的样板文件。这些具体的改进是本文后面讨论的模板更改的基础。您可以在.NET 6的示例中看到本节中使用的示例。我将在.NET 6的最后一个帖子中添加更多的例子。

Record structs

C# 10增加了对Record structs的支持。这个新特性类似于C# 9(基于类的)记录,但有一些主要的区别。在大多数情况下,添加Record structs是为了完整性,这样结构就可以享受与类相同的记录优势。然而,该团队并没有简单地结构化记录,而是决定将结构记录与ValueTuple保持一致,就像类记录匹配一致。由于这种设计方法,Record structs的属性在默认情况下是可变的,而Record类属性是不可变的。但是,您可以声明一个只读的Record structs,它是不可变的,并匹配记录类语义。

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

让我们看看Record structs与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 structs的示例产生如下结果。

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

如前所述,最明显的区别是记录结构属性在默认情况下是可变的。这是除了结构体和记录结构体语法之外的主要区别。我还用一个只读记录结构重写了这个示例,如下所示。

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示例相同。

让我们回顾一下C# 9的记录。它们提供了一种简洁的语法为定义类似结构的面向数据的类。他们偏向于不变性,同时提供简洁的语法——带有表达式——为了不可变友好的复制。人们可能会惊讶于我们开始使用类来实现类似结构的功能。大多数时候,开发人员使用类而不是结构,这是由于引用传递而不是值语义。在大多数情况下,类是最好的选择,而且易于使用。

结构记录与类记录非常相似:

  • 它们使用相同的语法(除了定义中的struct或class)。
  • 它们允许自定义成员定义(在C#10中新增)以在(默认情况下)属性成员上使用字段。
  • 它们允许使用init或可变属性定制成员行为。
  • 它们支持表达式。事实上,从C# 10开始,所有的结构类型都支持表达式。

结构记录不同于类记录:

  • Record structs可以用Record structs或只读Record structs来定义。
  • 记录类是用记录或记录类定义的。
  • 默认情况下,Record structs属性是可变的(get/set)。
  • 默认情况下,记录类属性是不可变的(get/init)。

Record structs和Record类之间的非对称(不)可变性行为可能会让一些读者感到惊讶,甚至厌恶。我会试着解释设计背后的想法。由于通过值传递语义,结构从不变性中获得的好处几乎不如类。例如,如果结构体是字典中的键,则该(结构体的副本)键是不可变的,只能将通过相等执行查找。与此同时,设计团队认为ValueTuple可以被视为一个匿名的记录结构,而Record structs可以被视为ValueTuple的衍生。只有当记录结构包含可变性和支持字段时,这才有效,这正是团队决定要做的。另外,包含可变性反映了结构和类的不同。

如果您更喜欢记录结构的不可变行为,您可以通过添加 readonly 关键字来获得它。

Looking at structs generally, key functionality is common:

  • 运行时提供的等同性检查对所有结构都是相同的。
  • 所有的结构都可以和表达式一起用来创建非变异的副本,这是C# 10中的新功能。

Global usings

global using使您可以指定一个你想要在所有源文件中可用的命名空间,就像在每个源文件中都声明了它一样。它也可以用于使用静态和别名一起使用。该特性允许使用一组通用的using声明,并扩展为不再需要的更多的using行。这与命名名称空间最为相关,但可以用于任何命名空间。

以下语法可用于各种使用形式:

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

这些声明只需要在编译中声明一次就可以了。有四种模式声明global using。

  • 在 Program.cs 中,通过将根 using 语句升级为global using 来使它们对整个程序具有全局性。
  • 在globaluses .cs文件(或类似的名称)中,集中管理所有的global using语句。
  • 在项目文件中,使用下面的语法。
  • 在你的项目文件中,使用语句(对于你的应用所依赖的MSBuild SDK)启用默认平台,语法如下。

下面的MSBuild语法可以在(使用与前面示例类似的方法)中代替.cs文件。

您可以在 中启用由平台定义的隐式 using 语句。

  • enable

隐式使用不同的MSBuild SDK。Microsoft.NET.Sdk定义了一个基本集,其他sdk定义附加集。
如果使用MSBuild功能,您可以在obj目录中生成的文件中看到有效的结果,如下所示。

PS C:\Users\rich\app> type .\app.csproj | findstr Using
    enable
    
    
PS C:\Users\rich\app> type .\obj\Debug\net6.0\app.GlobalUsings.g.cs
// 
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语句。请注意,全局的::操作符不是必需的,为了理解生成的语法,可以放心地忽略它。

文件与命名空间声明

文件作用域命名空间的声明是C# 10的另一个特性,旨在减少缩进和行数。

此功能的语法如下:

namespace Foo;

它是传统三行语法的替代方案:

namespace Foo
{
}

三行语法可以嵌套。单行语法不支持嵌套。每个文件只能有一个文件作用域的声明。它必须在文件中定义的所有类型之前,很像三行语法。
命名空间与顶级语句不兼容。顶级语句存在于顶级命名空间中。这还意味着,如果您向Program类添加额外的方法,使用partial类语法,则partial Program类也需要位于顶级命名空间中。
这个特性非常类似于添加到C# 8中的单行using声明

Const和内插字符串

现在您可以将内插字符串分配给 const 变量。插值字符串使用和读取都很直观,在任何地方都可用。现在,它们可以与 const 一起使用,前提是占位符值也是常量。

以下示例演示了一个可能使用的例子:

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

字符串插值进行了许多其他改进,在 C# 10 和 .NET 6 中的字符串插值中进行了描述。

扩展属性模式

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

{ Prop1.Prop2: pattern }

以前,你需要使用更详细的形式:

{ Prop1: { Prop2: pattern } }

您可以在下面的示例中看到使用这种更紧凑的形式,例如使用Reading.PM25。

List 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#特性和模式。对这些更改有重要反馈,部分原因是构建开始失败。作为初始模板更新的一部分,我们默认为.NET6(NET 6.0)项目(包括从.NET 5更新到.NET 6的应用程序)启用了隐式使用(又名opt-out)。那已经改变了。我们已经更新了SDK,所以所有的新功能都是可选的。对这个变化(在RC1中做出的)的响应是积极的。

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

新模板中使用了以下语言特性:

  • 异步Main
  • 顶级语句
  • Target-typed新表达式
  • global using指令
  • 文件作用域命名空间
  • 可空引用类型

我们构建了所有这些功能,因为我们认为它们比之前的替代方案更好。模板是引导新开发人员和新应用程序使用最佳模式的最简单和最好的方法。C#设计团队坚信使用更少的行数、更少的字符来指定给定的概念或操作,并减少不必要的重复。这就是大多数这些新功能所支持的。Nullable 的不同之处在于它产生更可靠的代码。每个使用nullable的应用程序或库都不太可能在生产中崩溃。软件太复杂了,人类无法像编译器那样看到错误。

这些功能的一个共同主题是,当您在代码编辑器中查看代码时,它们会减少干扰并增加提示。现在,更加重要的方面会弹出。例如,如果您在给定的文件中看到using语句,那么它是隐式集之外需要的特殊语句。你应该能够复制/粘贴代码从一个文件到另一个不需要CTRL-.类型以添加所需的名称空间(至少没有那么多)。如果您看到可空的警告或错误,您就知道您的代码可能在某些方面是不正确的。去除缩进还有好处。更多的代码将在编辑器的前十几个列中可见,而不是仅仅用空格字符填充这些列。人们可以把这些特征看作是在向大脑传递信息的过程中,向眼睛传递更高密度的特异性和意义。

控制台模板

让我们从控制台模板开始。这是非常小的。

Program.cs:
// 查看 https://aka.ms/new-console-template 得到更多信息
Console.WriteLine("Hello, World!");
Project 文件:

  
    Exe
    net6.0
    enable
    enable
  

尽管控制台模板比它的.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.cs 和 Startup.cs 之间的分离已被删除。不再需要 Startup.cs。

Program.cs:
var builder = WebApplication.CreateBuilder(args);
// 添加 services to the container.
builder.Services.AddControllers();
// 学习更多关于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 模板的项目文件与控制台类似。


  
    net6.0
    enable
    enable
  
  
    
  

您可以在本示例中看到文件作用域命名空间被使用,包括在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; }
}

目标类型 new 与 WeatherForecastController.cs 一起使用:

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

在mvc模板中,你可以看到可空注解和表达式体方法的使用。

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
{
    /// 
    ///  The main entry point for the application.
    /// 
    [STAThread]
    static void Main()
    {
        ApplicationConfiguration.Initialize();
        Application.Run(new Form1());
    }    
}

您可以看到使用了单行命名空间语句,而由于启用了隐式使用,因此没有任何平台级using语句。

WPF 模板尚未作为版本的一部分进行更新。

隐式使用

现在我将向您展示这些特性的实际应用。让我们从隐式用法开始。当启用时,每个Sdk都会添加自己的一组隐式using语句

如前所述,Microsoft.NET.Sdk添加了几个global using语句。

如果禁用该特性,您将看到应用程序不再编译,因为不再声明System命名空间(在本例中) 。

PS C:Usersrichapp> type .app.csproj | findstr Implicit
    disable
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.

另一种选择是使用global using特性,它允许您只使用您想要的名称空间,如下面的示例所示。


  
    Exe
    net6.0
    disable
    enable
  
  
    
  

现在,构建代码。

PS C:Usersrichapp> type .app.csproj | findstr Using
    disable
  
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包中利用命名空间的显式global using。

可空性

我已经更新了Program.cs,用来演示可空的引用类型。应用程序调用List方法,返回一个T?,在本例中是一个可空字符串(string?)

List greetings = new()
{
    "Nice day, eh?"
};
string? hello = greetings.Find(x => x.EndsWith("!"));
string greeting = hello ?? "Hello world!";
Console.WriteLine($"There are {greeting.Length} characters in "{greeting}"");

定义hello变量的行如果没有定义为var, string?或者List.Find返回null,那么就不能够被编译. 如果没有启用nullable特性,我可能会错过这个问题,这将导致我的代码因NullReferenceException异常而崩溃。那就不好了。我在下一行使用?? 和空合并运算符。在大多数情况下,这两行代码会合并为一行,如下面的代码所示。考虑到返回可空引用类型的API, 我将它们分开(在这个人为的示例中),以便您可以看到我使用的string?

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

在本例中,我将所有内容合并到一行中。我现在可以将变量声明为字符串,因为null已经适应了??后面的字符串。字符串?在这种情况下,只有编译器才能看到。

string[] args

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

string greeting = args.Length > 0 ? string.Join(" ", args) : "Hello World!";
WriteLine($"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类型仍然是被定义的。Program.Main不是。通过添加一个全局静态using到我的项目文件,我从Program.cs中的Console.WriteLine中删除了Console,如下所示。


  
    Exe
    net6.0
    enable
    enable
  
  
    
  

定义常规方法

我们已经听到一些用户的反馈,他们认为真正的方法优于局部函数,特别是对于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 $"There are {greeting.Length} characters in "{greeting}" in this {nameof(Program)}.";
    }
}

它产生与前面示例相同的结果。Program.Foo.cs文件名是任意的。事实上,Program.cs也是如此。你可以给这些文件起任何你喜欢的名字。编译器会找出他们。

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

macOS and Windows Arm64 更新

我们除了有好消息,也有一些突破性的变化要分享。支持macOS和Windows arm64的项目已经基本完成。在macOS和Windows团队的帮助下,我们已经在这上面工作了一年多。起初,我们认为该项目只是为了在 macOS 上支持 Arm64,而 Rosetta 2 将涵盖 x64。很简单,就像ISAs,对吧?不完全是。随着我们深入研究,我们发现x64 + Arm64共存是一个更大的任务,主要集中在CLI和安装程序上,包括一些他们需要达成一致的地方。借助 RC2,我们正在交付 .NET 的构建版本,以提供我们可以想象的最佳体验。

我将快速总结一下我们在 macOS 和 Windows Arm64 machines的进程:

  • .NET 6 RC2 通过将 Arm64 和 x64 版本安装到不同位置来实现 Arm64 + x64 共存。到目前为止,Arm64和x64构建相互覆盖,which led to general sadness。
  • 您需要卸载所有 .NET 版本并从头开始(在 macOS 和 Windows Arm64 机器上)以采用 .NET 6 RC2+。这包括 Arm64 和 x64、.NET 6 和 .NET 6 之前的版本。
  • .NET 6 之前的版本尚未准备好安装。
  • CLI允许您使用Arm64 SDK进行Arm64和x64开发(假设您已经安装了所需的Arm64和x64运行时)。反过来也是一样。
  • 我们希望人们只使用Arm64 SDK,因为它会有更好的体验(本机架构性能;只有一个SDK需要维护)。我们将继续改进产品,使这一模式成为大多数开发者的简单选择。
  • 对于SDK,我们只在Arm64上支持.NET 6+。早期的SDK构建将在Arm64上被阻止
  • 对于运行时,我们将支持所有支持的版本,Arm64 和 x64。
  • .NET 6 RC2为Arm64(包括x64仿真)提供了大量的.NET 6最终体验。
  • 我们希望更新.NET Core 3.1和.NET 5运行时,以与.NET 6 RTM保持一致(在时间上和技术上)。这还待定。
  • RC2的夜间构建目前是坏的,所以您需要再等几周,直到我们真正发布RC2来尝试所有这些。
  • 针对Windows Arm64的.NET 5 SDK将会随着.NET 6 RTM而提早退出支持。

我们也在考虑对dotnet test做一些突破性的改变,以统一我们用于架构目标的观点:

[[[Breaking change] For dotnet test, switch -a to alias –arch instead of –test-adapter-path]](https://github.com/dotnet/sdk...)

[[Breaking change] For dotnet test, switch -r to alias –runtime instead of –results-dir](https://github.com/dotnet/sdk...)

该项目的很大一部分是通过 Arm64 SDK 启用 x64 运行时。您可以在下图中看到这一点。

官宣 .NET 6 RC (Release Candidate) 2_第1张图片

如果你想了解更多细节,请查看dotnet/sdk #21686

总结

C# 10基于C# 9的类似特性,在简单性和可表达性方面提供了显著的改进。过去,人们可以合理地嘲笑C#,因为仅仅编写一行代码就需要这么多的礼节和面向对象概念的知识。那些日子已经过去了,模板反映了这一点。这些变化的部分动机是让C#对新程序员和学校更有吸引力。这些简化的改变从根本上改变了您开始学习和熟练使用C#所需要的东西。在新的默认形式下,它可以直接与其他类似地以单一文件源文件开始的语言相比较。

我们期待看到其他项目通过顶级语句、全局使用、记录和许多其他现代特性来简化他们的新用户体验。有很多机会可以为开源项目和商业产品创建更简单的学习旅程。

我们鼓励您使用 .NET 6RC2。我们相信您会发现它是一个可靠的版本。并将 全球.NET Conf 的日期会在 11 月 9 日,以便 .NET 6 发布。

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

你可能感兴趣的:(.net)