C# 12 中的新增功能

C# 12 包含以下新功能。 可以使用最新的 Visual Studio 2022 版本或 .NET 8 SDK来试用这些功能。

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

可以从 .NET 下载页面下载最新的 .NET 8 SDK。 还可以下载 Visual Studio 2022,其中包括 .NET 8 SDK。

注意

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

主构造函数

现在可以在任何 classstruct中创建主构造函数。 主要构造函数不再局限于 record 类型。 主构造函数参数在整个类体的范围内有效。 为了确保明确分配所有主构造函数参数,所有显式声明的构造函数都必须使用 this() 语法调用主构造函数。 将主构造函数添加到 class 可防止编译器声明隐式无参数构造函数。 在 struct中,隐式无参数构造函数将所有字段(包括主构造函数参数)初始化为 0 位模式。

编译器仅在 record 类型(record classrecord struct 类型)中为主构造函数参数生成公共属性。 非记录类和结构可能并不总是需要主构造函数参数的此行为。

可以在教程“探索主构造函数”中了解有关主要构造函数的详细信息,以及在文章“关于实例构造函数”中了解相关内容。

集合表达式

集合表达式引入了新的 terse 语法来创建常见的集合值。 可以使用分布元素 ..e将其他集合内联到这些值中。

无需外部 BCL 支持即可创建多个类似集合的类型。 这些类型包括:

以下示例演示集合表达式的使用:

// Create an array:
int[] a = [1, 2, 3, 4, 5, 6, 7, 8];

// Create a list:
List<string> b = ["one", "two", "three"];

// Create a span
Span<char> c  = ['a', 'b', 'c', 'd', 'e', 'f', 'h', 'i'];

// Create a jagged 2D array:
int[][] twoD = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];

// Create a jagged 2D array from variables:
int[] row0 = [1, 2, 3];
int[] row1 = [4, 5, 6];
int[] row2 = [7, 8, 9];
int[][] twoDFromVariables = [row0, row1, row2];

集合表达式中的 分布元素..e 添加该表达式中的所有元素。 参数必须是集合类型。 以下示例演示分布元素的工作原理:

int[] row0 = [1, 2, 3];
int[] row1 = [4, 5, 6];
int[] row2 = [7, 8, 9];
int[] single = [.. row0, .. row1, .. row2];
foreach (var element in single)
{
    Console.Write($"{element}, ");
}
// output:
// 1, 2, 3, 4, 5, 6, 7, 8, 9,

spread 元素计算枚举表达式的每个元素。 每个元素都包含在输出集合中。

可以在需要元素集合的任何位置使用集合表达式。 它们可以指定集合的初始值,也可以作为参数传递给采用集合类型的方法。 可以在 有关集合表达式的语言参考文章 或 C# 标准的 §12.8.25 集合表达式 子句中了解有关集合表达式的详细信息。

ref readonly 个参数

C# 添加了 in 参数作为传递只读引用的方法。 in 参数同时允许变量和值,并且无需对参数进行任何注释即可使用。

添加 ref readonly 参数可以更清楚地了解可能使用 ref 参数或 in 参数的 API:

若要了解有关 ref readonly 参数的详细信息,请参阅有关语言参考中 参数修饰符 的文章,或 ref 只读参数 功能规范。

默认 lambda 参数

现在可以在 lambda 表达式上定义参数的默认值。 语法和规则与向任何方法或本地函数添加参数的默认值相同。

您可以在有关 lambda 表达式的文章中了解有关默认参数的更多信息。

任何类型的别名

可以使用 using 别名指令创建任何类型的别名,而不仅仅是命名类型。 这意味着可以为元组类型、数组类型、指针类型或其他不安全类型创建语义别名。 有关详细信息,请参阅 §14.6.2 在 C# 标准中使用别名指令 。 有关示例重构演练,请参阅使用 .NET 博客上的任意类型别名重构代码

内联数组

运行时团队和其他库作者使用内联数组来提高应用中的性能。 内联数组使开发人员能够在 struct 类型中创建固定大小的数组。 具有内联缓冲区的结构应提供类似于不安全固定大小缓冲区的性能特征。 你可能不会声明自己的内联数组,但在运行时 API 中将其公开为 System.Span<T>System.ReadOnlySpan<T> 对象时,以透明方式使用这些数组。

内联数组的声明类似于以下 struct

[System.Runtime.CompilerServices.InlineArray(10)]
public struct Buffer
{
    private int _element0;
}

它们的用法与任何其他数组类似:

var buffer = new Buffer();
for (int i = 0; i < 10; i++)
{
    buffer[i] = i;
}

foreach (var i in buffer)
{
    Console.WriteLine(i);
}

区别在于编译器可以利用有关内联数组的已知信息。 你可能会像使用任何其他数组一样使用内联数组。 有关如何声明内联数组的详细信息,请参阅有关 struct 类型的语言参考

Experimental 属性

可以使用 System.Diagnostics.CodeAnalysis.ExperimentalAttribute 标记类型、方法或程序集,以指示实验性功能。 如果访问使用 ExperimentalAttribute注释的方法或类型,编译器将发出警告。 标记为 Experimental 属性的程序集中包含的所有类型都是实验性的。 可以在有关编译器 读取的常规属性或 功能规范的文章中阅读详细信息。

拦截 器

警告

侦听器是一项试验性功能,在 C# 12 的预览模式下可用。 在将来的版本中,该功能可能会发生重大更改或删除。 因此,不建议用于生产环境或已发布的应用程序。

若要使用侦听器,用户项目必须指定属性 <InterceptorsPreviewNamespaces>。 这是允许包含侦听器的命名空间列表。

例如:<InterceptorsPreviewNamespaces>$(InterceptorsPreviewNamespaces);Microsoft.AspNetCore.Http.Generated;MyLibrary.Generated</InterceptorsPreviewNamespaces>

拦截器是一种方法,该方法可以在编译时以声明方式将对可拦截方法的调用替换为对其自身的调用。 通过让侦听器声明其截获的调用的源位置,将发生此替换。 侦听器通过向编译添加新代码(例如在源生成器中)提供一个有限的设施来更改现有代码的语义。

使用 拦截器 作为源生成器的一部分来修改现有源编译,而不是添加代码。 源生成器用对 拦截器 方法的调用替换对可拦截方法的调用。

如果有兴趣尝试拦截器,可以通过阅读 功能规范来了解详细信息。 如果使用该功能,请确保随时了解此实验功能的功能规范中的任何更改。 如果功能最终完成,我们将在此网站上添加更多指导。

另请参阅