从此产品 专题 文章 上网

C# 11、.NET 7、.NET MAUI 7 官方发布

C# 11 发布公告称,“随着每个版本的发布,社区的参与度越来越高,贡献了从建议、见解和错误报告到整个功能实现的所有内容。这真的是每个人的 C#。”

新版本中的一些亮点更新内容包括:

UTF-8 字符串(UTF-8 String Literals)

默认情况下,C# 字符串被硬编码为 UTF-16,而 Internet 上通行的字符串编码是 UTF-8。为了最大限度地减少转换的麻烦和性能开销,现在可以简单地为你的 string literals 附加一个u8后缀,以便立即将它们转换为 UTF-8:

var u8 = "This is a UTF-8 string!"u8;

UTF-8 string literals 只是给你返回一个字节块 —— 以ReadOnlySpan<byte>形式。对于那些需要 UTF-8 编码的场景,这可能比一些专门的新的 UTF-8 字符串类型更有用。

阅读有关 UTF-8 字符串文字的文档。

原始字符串 (Raw string literals)

从 C# 11 开始,可以使用原始字符串字面量更轻松地创建多行字符串,或使用需要转义序列的任何字符。 原始字符串字面量无需使用转义序列。 你可以编写字符串,包括空格格式,以及你希望在输出中显示该字符串的方式。 原始字符串字面量:

Raw string literals 由至少三个双引号分隔:

var raw1 = """This\is\all "content"!""";
Console.WriteLine(raw1);

This prints:

This\is\all "content"!

如果你需要三个或更多的"s 成为你内容的一部分,只需在外部使用更多的"s。开头和结尾必须一致。

var raw2 = """""I can do ", "", """ or even """" double quotes!""""";

这使得粘贴、维护和一目了然地阅读文字所包含的内容变得非常容易。

多行 raw string literals 也可以截断前导空格:末端引号的位置决定了空格开始包含在输出中的位置:

var raw3 = """
    <element attr="content">
      <body>
        This line is indented by 4 spaces.
      </body>
    </element>
    """;
//  ^white space left of here is removed

由于末端引号的左侧有四个空格,因此将从每行内容的开头删除四个空格,从而产生以下输出:

<element attr="content">
  <body>
    This line is indented by 4 spaces.
  </body>
</element>

此外,Raw string literals 还支持插值,可在文档中阅读有关的更多信息。

对静态成员进行抽象

在 C# 11 中发布了对接口中静态虚拟成员的支持,此功能在 C# 10 中处于预览状态。有了它,你现在可以定义一个非常简单的数学接口:

public interface IMonoid<TSelf> where TSelf : IMonoid<TSelf>
{
    public static abstract TSelf operator +(TSelf a, TSelf b);
    public static abstract TSelf Zero { get; }
}

任何人现在都可以通过为两个静态成员提供实现并将自己作为TSelf类型参数传递来实现此接口:

public struct MyInt : IMonoid<MyInt>
{
    int value;
    public MyInt(int i) => value = i;
    public static MyInt operator +(MyInt a, MyInt b) => new MyInt(a.value + b.value);
    public static MyInt Zero => new MyInt(0);
}

重要的是,你如何使用这些抽象的操作?当没有实例可供调用时,你如何调用虚拟成员?答案是通过泛型:

T AddAll<T>(params T[] elements) where T : IMonoid<T>
{
    T result = T.Zero;
    foreach (var element in elements)
    {
        result += element;
    }
    return result;
}

类型参数TIMonoid<T>接口约束,这使得该接口的静态虚拟成员Zero+可以在Titself 上被调用。

现在我们可以用一些MyInt来调用泛型方法,而+Zero的正确实现则通过类型参数传递进来:

MyInt sum = AddAll<MyInt>(new MyInt(3), new MyInt(4), new MyInt(5));

事实上,.NET 7 提供了一个新的命名空间 System.Numerics,其中充满了数学接口,代表了不同的运算符组合和其他你想使用的静态成员:上述小 IMonoid<T> 接口的 "grown-up" 版本。.NET 中的所有数字类型现在都实现了这些新的接口 -- 你也可以为你自己的类型添加这些接口。

还值得注意的是,静态虚拟成员对数学以外的其他事物也很有用。更多详情可在有关静态抽象接口方法和通用数学的文档中查看。即使你不使用静态虚拟成员创建接口,也可以从他们现在和将来对 .NET 库所做的改进中受益。

列表模式(List patterns)

Pattern matching 是在 C# 7 中引入的,从那时起它已发展成为该语言中最重要和最强大的控制结构之一;C# 11 添加了列表模式从 C# 11 开始,可以将数组或列表与模式的序列进行匹配,如以下示例所示:

int[] numbers = { 1, 2, 3 };

Console.WriteLine(numbers is [1, 2, 3]);  // True
Console.WriteLine(numbers is [1, 2, 4]);  // False
Console.WriteLine(numbers is [1, 2, 3, 4]);  // False
Console.WriteLine(numbers is [0 or 1, <= 2, >= 3]);  // True

如前面的示例所示,当每个嵌套模式与输入序列的相应元素匹配时,列表模式就会匹配。 可使用列表模式中的任何模式。 若要匹配任何元素,请使用弃元模式,或者,如果还想捕获元素,请使用 var 模式,如以下示例所示:

List<int> numbers = new() { 1, 2, 3 };

if (numbers is [var first, _, _])
{
    Console.WriteLine($"The first element of a three-item list is {first}.");
}
// Output:
// The first element of a three-item list is 1.

前面的示例将整个输入序列与列表模式匹配。 若要仅匹配输入序列开头或 / 和结尾的元素,请使用列表模式中的切片模式..,如以下示例所示:

Console.WriteLine(new[] { 1, 2, 3, 4, 5 } is [> 0, > 0, ..]);  // True
Console.WriteLine(new[] { 1, 1 } is [_, _, ..]);  // True
Console.WriteLine(new[] { 0, 1, 2, 3, 4 } is [> 0, > 0, ..]);  // False
Console.WriteLine(new[] { 1 } is [1, 2, ..]);  // False

Console.WriteLine(new[] { 1, 2, 3, 4 } is [.., > 0, > 0]);  // True
Console.WriteLine(new[] { 2, 4 } is [.., > 0, 2, 4]);  // False
Console.WriteLine(new[] { 2, 4 } is [.., 2, 4]);  // True

Console.WriteLine(new[] { 1, 2, 3, 4 } is [>= 0, .., 2 or 4]);  // True
Console.WriteLine(new[] { 1, 0, 0, 1 } is [1, 0, .., 0, 1]);  // True
Console.WriteLine(new[] { 1, 0, 1 } is [1, 0, .., 0, 1]);  // False

切片模式匹配零个或多个元素。 最多可在列表模式中使用一个切片模式。

还可以在切片模式中嵌套子模式,如以下示例所示:

void MatchMessage(string message)
{
    var result = message is ['a' or 'A', .. var s, 'a' or 'A']
        ? $"Message {message} matches; inner part is {s}."
        : $"Message {message} doesn't match.";
    Console.WriteLine(result);
}

MatchMessage("aBBA");  // output: Message aBBA matches; inner part is BB.
MatchMessage("apron");  // output: Message apron doesn't match.

void Validate(int[] numbers)
{
    var result = numbers is [< 0, .. { Length: 2 or 4 }, > 0] ? "valid" : "not valid";
    Console.WriteLine(result);
}

Validate(new[] { -1, 0, 1 });  // output: not valid
Validate(new[] { -1, 0, 0, 1 });  // output: valid

更多详情可查看相关文档。

Required members

 

开发团队已经在多个版本中进行了改进对象的创建和初始化的工作,C# 11 则通过 required members 来继续这些改进。

在创建使用对象初始化器的类型时,曾经无法指定必须初始化某些属性。现在,可以说一个属性或字段是required。这意味着,当创建该类型的对象时,它必须被对象初始化器初始化:

public class Person
{
    public required string FirstName { get; init; }
    public string? MiddleName { get; init; }
    public required string LastName { get; init; }
}

现在在不初始化两个必需属性的情况下创建一个Person是错误的:

var person = new Person { FirstName = "Ada" }; // Error: no LastName!

查看 required members 文档以获取更多信息。


微软宣布正式推出 .NET 7 ,使用 .NET 7 可以轻松地将 .NET 7 项目容器化,在 GitHub 操作中设置 CI/CD 工作流,并实现云原生可观察性。

.NET 7 是标准期限支持 (STS) 版本,将支持 18 个月。在整个 .NET 7 版本中,超过 8900 名贡献者做出了 28k 代码贡献。

.NET 7 的优化主要集中在:

一个基类库(Base Class Library - BCL)

使用 .NET 7,只需学习一次,就可以通过一个 SDK、一个运行时、一组基础库重复构建多种类型的应用程序(云、Web、桌面、移动、游戏、IoT 和 AI)。

对 ARM64 的原生支持

优化了在 Arm 机器上的性能,包括估算 L3 缓存大小、引入了 LSE 原子指令、优化使用内在函数的库。

详情可查看 .NET 7 中的 Arm64 性能改进。

Linux 上增强的 .NET 支持

.NET 6 包含在 Ubuntu 22.04 (Jammy) 中,可以使用apt install dotnet6命令安装。此外,还有一个优化的、预构建的、开箱即用的超小型容器镜像。

dotnetapp % docker run --rm dotnetapp-chiseled 42 42 ,d ,d 42 42 42 ,adPPYb,42 ,adPPYba, MM42MMM 8b,dPPYba, ,adPPYba, MM42MMM a8" `Y42 a8" "8a 42 42P' `"8a a8P_____42 42 8b 42 8b d8 42 42 42 8PP""""""" 42 "8a, ,d42 "8a, ,a8" 42, 42 42 "8b, ,aa 42, `"8bbdP"Y8 `"YbbdP"' "Y428 42 42 `"Ybbd8"' "Y428 .NET 7.0.0-preview.7.22375.6 Linux 5.10.104-linuxkit #1 SMP PREEMPT Thu Mar 17 17:05:54 UTC 2022 OSArchitecture: Arm64 ProcessorCount: 4 TotalAvailableMemoryBytes: 3.83 GiB

有关微软与 Canonical 和 ARM 合作的更多信息,请阅读 .NET 6 now in Ubuntu 22.04。

64 位 IBM Power 支持

除了 x64 架构(64 位 Intel/AMD)、ARM64(64 位 ARM)和 s390x(64 位 IBM Z)之外,.NET 也可用于针对 RHEL 的 ppc64le(64 位 IBM Power)架构 8.7 和 RHEL 9.1。

NET MAUI

NET MAUI 已是 .NET 7 的一部分,具有大量改进和新功能。可查看 .NET MAUI 的状态 和项目路线图,以获取更多信息。

现代化

.NET SDK 支持输出容器图像,有关内置容器支持的更多信息,请参阅对 .NET SDK 的内置容器支持。

升级助手

.NET 升级助手包含更多分析器、代码修复程序,以及对更多应用程序类型的升级支持,可帮助开发者更轻松地升级 .NET 7 应用程序组合,减少升级应用的工作量。

.NET 云原生

.NET 7 更易于构建和部署分布式云原生应用。


.NET MAUI (.NET Multi-platform App UI) 是一个跨平台 UI 框架(前身是 Xamarin.Forms),用于通过 C# 和 XAML 创建原生移动和桌面应用。基于 .NET MAUI,开发者可在单个共享代码库中创建同时支持 Android、iOS、macOS 和 Windows 的原生应用。

更新亮点

此版本推出了从 Xamarin.Forms 更新而来的 .NET MAUI Map 控件。和其他 UI 控件一样,这是由每个平台提供的原生 Map 控件的跨平台抽象。Map 控件支持标记、自定义标记、绘制多边形、折线和圆环、添加地理编码和地理定位,还可以在正在运行的设备上启动原生 Map 应用程序。

开发团队表示,经过短短 6 个月的开发,.NET MAUI 7 的速度比版本 6 快了不少,此版本优化了基本视图的渲染路径,并解决了影响 CollectionView 列表控件滚动流畅度的多个问题。

.NET MAUI 团队称他们一直与使用 .NET MAUI 构建桌面应用程序的公司密切合作,并够根据他们的实际使用情况引入了部分面向桌面端的增强功能,比如: