C# 11 中的新增功能

C# 11 中增加了以下功能:

.NET 7 支持 C# 11。 有关详细信息,请参阅 C# 语言版本控制

可以通过 .NET 下载页下载最新 .NET 7 SDK。 还可以下载 Visual Studio 2022,其中包括 .NET 7 SDK。

注意

我们有兴趣了解你对这些功能的反馈。 如果发现这些新功能存在问题,请在 dotnet/roslyn 存储库中创建新问题

泛型属性

可以声明基类为 System.Attribute泛型类。 此功能为需要 System.Type 参数的属性提供了更方便的语法。 以前需要创建一个属性,该属性将 Type 作为其构造函数参数:

// Before C# 11:
public class TypeAttribute : Attribute
{
   public TypeAttribute(Type t) => ParamType = t;

   public Type ParamType { get; }
}

并且为了应用该属性,需要使用 typeof 运算符:

[TypeAttribute(typeof(string))]
public string Method() => default;

使用此新功能,可以改为创建泛型属性:

// C# 11 feature:
public class GenericAttribute<T> : Attribute { }

然后指定类型参数以使用该属性:

[GenericAttribute<string>()]
public string Method() => default;

应用属性时,必须提供所有类型参数。 换句话说,泛型类型必须完全构造。 在上面的示例中,可以省略空括号 (()),因为特性没有任何参数。

public class GenericType<T>
{
   [GenericAttribute<T>()] // Not allowed! generic attributes must be fully constructed types.
   public string Method() => default;
}

类型参数必须满足与 typeof 运算符相同的限制。 不允许使用需要元数据注释的类型。 例如,不允许将以下类型用作类型参数:

  • dynamic
  • string?(或任何可为 null 的引用类型)
  • (int X, int Y)(或使用 C# 元组语法的任何其他元组类型)。

这些类型不会直接在元数据中表示出来。 它们包括描述该类型的注释。 在所有情况下,都可以改为使用基础类型:

  • object(对于 dynamic)。
  • string,而不是 string?
  • ValueTuple<int, int>,而不是 (int X, int Y)

泛型数学支持

有几个语言功能支持泛型数学支持:

  • static virtual 接口中的成员
  • 已检查的用户定义的运算符
  • 宽松移位运算符
  • 无符号右移运算符

可以在接口中添加 static abstractstatic virtual 成员,以定义包含可重载运算符、其他静态成员和静态属性的接口。 此功能的主要场景是在泛型类型中使用数学运算符。 例如,可以在实现 operator + 的类型中实现 System.IAdditionOperators<TSelf, TOther, TResult> 接口。 其他接口定义其他数学运算或明确定义的值。 可以在有关接口的文章中了解新语法。 包含 static virtual 方法的接口通常是泛型接口。 此外,大部分将声明一个约束,即类型参数实现声明接口

可以在探索静态抽象接口成员教程或 .NET 6 中的预览功能 - 泛型数学博客文章中了解详细信息并亲自尝试该功能。

泛型数学对语言创建了其他要求。

  • 无符号右移运算符:在 C# 11 之前,若要强制无符号右移,需要将任何带符号整数类型强制转换为无符号类型,执行移动,然后将结果强制转换回带符号类型。 从 C# 11 开始,可以使用 >>>无符号移动运算符
  • 宽松的移动运算符要求:C# 11 删除了第二个操作数必须是 int 或隐式转换为 int 的要求。 此更改允许在这些位置使用实现泛型数学接口的类型。
  • 已检查和未检查的用户定义运算符:开发人员现在可以定义 checkedunchecked 算术运算符。 编译器根据当前上下文生成对正确变体的调用。 有关 checked 运算符的详细信息,可以阅读有关算术运算符的文章。

数值 IntPtrUIntPtr

现在 nintnuint 类型的别名分别为 System.IntPtrSystem.UIntPtr

字符串内插中的换行符

字符串内插的 {} 字符内的文本现在可以跨多个行。 {} 标记之间的文本分析为 C#。 允许任何合法 C#(包括换行符)。 使用此功能可以更轻松地读取使用较长 C# 表达式的字符串内插,例如模式匹配 switch 表达式或 LINQ 查询。

可以在语言参考的字符串内插文章中了解有关换行符功能的详细信息。

列表模式

列表模式扩展了模式匹配,以匹配列表或数组中的元素序列。 例如,当 sequence 为数组或三个整数(1、2 和 3)的列表时,sequence is [1, 2, 3]true。 可以使用任何模式(包括常量、类型、属性和关系模式)来匹配元素。 弃元模式 (_) 匹配任何单个元素,新的范围模式 (..) 匹配零个或多个元素的任何序列。

可以在语言参考的模式匹配文章中了解有关列表模式的更多详细信息。

改进了方法组向委托的转换

方法组转换上的 C# 标准现在包含以下项:

  • 允许转换(但不是必需的)以使用已包含这些引用的现有委托实例。

以前版本的标准禁止了编译器重用为方法组转换而创建的委托对象。 C# 11 编译器缓存从方法组转换创建的委托对象,并重用该单个委托对象。 此功能最初在 Visual Studio 2022 版本 17.2 中作为预览功能提供,在 .NET 7 预览版 2 中首次提供。

原始字符串文本

原始字符串字面量是字符串字面量的一种新格式。 原始字符串字面量可以包含任意文本,包括空格、新行、嵌入引号和其他特殊字符,无需转义序列。 原始字符串字面量以至少三个双引号 (""") 字符开头。 它以相同数量的双引号字符结尾。 通常,原始字符串字面量在单个行上使用三个双引号来开始字符串,在另一行上用三个双引号来结束字符串。 左引号之后、右引号之前的换行符不包括在最终内容中:

string longMessage = """
    This is a long message.
    It has several lines.
        Some are indented
                more than others.
    Some should start at the first column.
    Some have "quoted text" in them.
    """;

右双引号左侧的任何空格都将从字符串字面量中删除。 原始字符串字面量可以与字符串内插结合使用,以在输出文本中包含大括号。 多个 $ 字符表示有多少个连续的大括号开始和结束内插:

var location = $$"""
   You are at {{{Longitude}}, {{Latitude}}}
   """;

前面的示例指定了两个大括号开始和结束内插。 第三个重复的左大括号和右大括号包括在输出字符串中。

可以在编程指南中关于字符串的文章中,以及关于字符串字面量内插字符串的语言参考文章中详细了解原始字符串字面量。

自动默认结构

C# 11 编译器可以确保在执行构造函数的过程中,将 struct 类型的所有字段初始化为其默认值。 此更改意味着,任何未由构造函数初始化的字段或自动属性都由编译器自动初始化。 现在,构造函数未明确分配所有字段的结构可以进行编译,并且未显式初始化的任何字段都设置为其默认值。 有关此更改如何影响结构初始化的详细信息,请阅读有关结构的文章。

常量 string 上的模式匹配 Span<char>ReadOnlySpan<char>

你已经能够在几个版本中使用模式匹配来测试 string 是否有某个特定的常量值。 现在,可以将同一模式匹配逻辑用于 Span<char>ReadOnlySpan<char> 的变量。

扩展的 nameof 范围

在该方法的属性声明中的 nameof 表达式中使用时,类型参数名称和参数名称现在处于范围内。 此功能意味着可以使用 nameof 运算符在方法或参数声明的属性中指定方法参数的名称。 此功能最常用于为可为空分析添加属性。

UTF-8 字符串字面量

可以对字符串字面量指定 u8 后缀来指定 UTF-8 字符编码。 如果应用程序需要 UTF-8 字符串,则对于 HTTP 字符串常量或类似的文本协议来说,可以使用此功能来更轻松地创建 UTF-8 字符串。

有关 UTF-8 字符串字面量的详细信息,请参阅关于内置引用类型的文章的字符串字面量部分。

必需的成员

可以将 required 修饰符添加到属性和字段,以强制构造函数和调用方初始化这些值。 可以将 System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute 添加到构造函数,以通知编译器构造函数将初始化所有必需的成员。

有关所需成员的详细信息,请参阅属性文章的仅限初始化部分。

ref 字段和 ref scoped 变量

可以在 ref struct 中声明 ref 字段。 这支持没有特殊特性或隐藏的内部类型的 System.Span<T> 等类型。

可向任意 ref 声明添加 scoped 修饰符。 这限制了可将引用转义到的范围

文件本地类型

从 C# 11 开始,可以使用 file 访问修饰符创建其可见性范围限定为其声明时所在的源文件的类型。 此功能可帮助源生成器创建者避免命名冲突。 可以在语言参考中有关文件范围类型的文章中详细了解此功能。

另请参阅