C# 13 中的新增功能
C# 13 包括以下新增功能。 可以使用最新的 Visual Studio 2022 版本或 .NET 9 SDK 尝试这些功能:
params
集合- 新的
lock
类型和语义。 - 新的转义序列 -
\e
。 - 方法组自然类型改进
- 对象初始值设定项中的隐式索引器访问
- 在迭代器和异步方法中启用
ref
局部变量和unsafe
上下文 - 启用
ref struct
类型来实现接口。 - 允许在泛型中将 ref 结构类型 作为类型参数的参数。
-
partial
类型中现在允许使用部分属性和索引器。 - 重载解析优先级允许库作者指定一种重载优于其他重载。
从 Visual Studio 17.12 开始,C# 13 将 field
上下文关键字列为预览功能。
C# 13 支持在 .NET 9上。 有关详细信息,请参阅 C# 语言版本控制。
可以通过 .NET 下载页下载最新 9 .NET SDK。 还可以下载 Visual Studio 2022,其中包括 .NET 9 SDK。
新功能一旦在公开预览版中发布,就会被添加到“C# 新特性”页中。 roslyn 功能状态页的工作集部分跟踪即将推出的功能何时合并到主分支中。
可以在我们关于重大更改的文章中找到 C# 13 中引入的任何重大更改。
注意
我们有兴趣了解你对这些功能的反馈。 如果发现这些新功能存在问题,请在 dotnet/roslyn 存储库中创建新问题。
params
集合
params
修饰符并不局限于数组类型。 现在可以将 params
用于任何已识别的集合类型,包括 System.Span<T>、System.ReadOnlySpan<T>,以及那些实现 System.Collections.Generic.IEnumerable<T> 并具有 Add
方法的类型。 除了具体类型外,还可以使用接口 System.Collections.Generic.IEnumerable<T>、System.Collections.Generic.IReadOnlyCollection<T>、System.Collections.Generic.IReadOnlyList<T>、System.Collections.Generic.ICollection<T>和 System.Collections.Generic.IList<T>。
使用接口类型时,编译器会为所提供的参数合成存储。 可以在 params
集合的功能规范中了解更多信息。
例如,方法声明可以将跨度声明为 params
参数:
public void Concat<T>(params ReadOnlySpan<T> items)
{
for (int i = 0; i < items.Length; i++)
{
Console.Write(items[i]);
Console.Write(" ");
}
Console.WriteLine();
}
新锁定对象
.NET 9 运行时包含一种新的线程同步类型,即 System.Threading.Lock 类型。 该类型通过其 API 提供更好的线程同步。 Lock.EnterScope() 方法进入独占范围。 从中返回的 ref struct
支持退出独占范围的 Dispose()
模式。
C# lock
语句可识别锁定的目标是否为 Lock
对象。 如果是,则使用更新的 API,而不是使用 System.Threading.Monitor 的传统 API。 如果将 Lock
对象转换为另一种类型,编译器也会识别,并生成基于 Monitor
的代码。 可以在新锁定对象的功能规范中了解详细信息。
通过此功能,您可以通过更改对象 lock
的类型来获得新库类型的优势。 其他代码无需更改。
新的转义序列
你可以使用 \e
作为 ESCAPE
字符 Unicode U+001B
的字符文本转义序列。 以前,你使用的是 \u001b
或 \x1b
。 不建议使用 \x1b
,因为如果 1b
后面的下一个字符是有效的十六进制数字,则那些字符会成为转义序列的一部分。
方法组自然类型
此功能对涉及方法组的重载解析进行了少量优化。 方法组是一个方法,并且所有重载都具有相同的名称。 编译器以前的行为是为方法组构造完整的候选方法集。 如果需要自然类型,则自然类型是根据整套候选方法确定的。
新行为是在每个作用域内削减候选方法集,移除那些不适用的候选方法。 通常,被删除的方法是具有错误参数或不满足约束的泛型方法。 只有在没有找到候选方法的情况下,该过程才会继续到下一个外部作用域。 此过程更紧密地遵循重载决策的一般算法。 如果在给定作用域内找到的所有候选方法都不匹配,则该方法组没有自然类型。
您可以在 提案规范中查看更改的详细信息。
隐式索引访问
现在在对象初始化表达式中允许使用隐式“从末尾”索引运算符^
。 例如,你现在可以在对象初始值设定项中初始化数组,如以下代码所示:
public class TimerRemaining
{
public int[] buffer { get; set; } = new int[10];
}
var countdown = new TimerRemaining()
{
buffer =
{
[^1] = 0,
[^2] = 1,
[^3] = 2,
[^4] = 3,
[^5] = 4,
[^6] = 5,
[^7] = 6,
[^8] = 7,
[^9] = 8,
[^10] = 9
}
};
TimerRemaining
类包括一个 buffer
数组,其初始化长度为 10。 前面的示例使用“从末尾”索引操作符 (^
) 为数组赋值,从而有效地创建了一个从 9 开始倒数到 0 的数组。
在 C# 13 之前的版本中,对象初始化器中不能使用 ^
运算符。 你需要从前面开始为元素编制索引。
迭代器和 async
方法中的 ref
和 unsafe
该功能和以下两个功能使 ref struct
类型可以使用新的结构体。 除非你编写自己的 ref struct
类型,否则你不会使用这些类型。 你更可能会看到间接的好处,因为 System.Span<T> 和 System.ReadOnlySpan<T> 获得了更多的功能。
在 C# 13 之前,迭代器方法(使用 yield return
的方法)和 async
方法不能声明局部 ref
变量,也不能使用 unsafe
上下文。
在 C# 13 中,async
方法可以声明 ref
局部变量或 ref struct
类型的局部变量。 但是,无法跨 await
边界访问这些变量。 也不能跨越 yield return
边界访问它们。
这种放宽的限制使编译器可以在更多地方安全地使用 ref
本地变量和 ref struct
类型,并可验证其安全性。 可以在这些方法中安全地使用 System.ReadOnlySpan<T> 等类型。 如果违反了安全规则,编译器会告诉你。
同样,C# 13 允许在迭代器方法中使用 unsafe
上下文。 但是,所有 yield return
和 yield break
语句都必须在安全的上下文中。
allows ref struct
在 C# 13 之前,ref struct
类型不能声明为泛型或方法的类型参数。 现在,泛型类型声明可以添加反约束 allows ref struct
。 此反约束声明为该类型参数提供的类型参数可以是 ref struct
类型。 编译器会对该类型参数的所有实例执行 ref 安全规则。
例如,可以像下面的代码一样声明一个泛型类型:
public class C<T> where T : allows ref struct
{
// Use T as a ref struct:
public void M(scoped T p)
{
// The parameter p must follow ref safety rules
}
}
这样,System.Span<T> 和 System.ReadOnlySpan<T> 等类型就可以与泛型算法一起使用(如果适用)。 可以在 where
的更新以及有关泛型约束的编程指南文章中了解详细信息。
ref struct
接口
在 C# 13 之前,ref struct
类型不允许实现接口。 从 C# 13 开始,这些功能可以实现。 你可以声明 ref struct
类型实现某个接口。 但是,为了确保 ref 安全规则,ref struct
类型无法转换为接口类型。 这种转换是装箱转换,可能会违反 ref 安全性。 ref struct
中的显式接口方法声明只能通过类型参数访问,而该类型参数为 allows ref struct
。 此外,ref struct
类型必须实现接口中声明的所有方法,包括默认实现的方法。
有关 ref struct
类型 和新增 allows ref struct
泛型约束的更多信息,请参阅更新。
更多部分成员
可以在 C# 13 中声明 partial
属性和 partial
索引器。 部分属性和索引器通常遵循与 partial
方法相同的规则:创建一个 声明声明,一个实现声明。 这两种声明的签名必须匹配。 一个限制是,不能使用自动属性声明来实现部分属性。 未声明正文的属性被视为声明声明。
public partial class C
{
// Declaring declaration
public partial string Name { get; set; }
}
public partial class C
{
// implementation declaration:
private string _name;
public partial string Name
{
get => _name;
set => _name = value;
}
}
可以在有关部分成员的文章中了解详细信息。
重载解析优先级
在 C# 13 中,编译器识别 OverloadResolutionPriorityAttribute,以便优先选择一个重载而不是另一个。 库作者可以使用该属性确保新的、更好的重载比现有的重载更受青睐。 例如,可以添加性能更高的新重载。 你不想中断使用库的现有代码,但又希望用户在重新编译时更新到新版本。 可以使用 重载解析优先级 来指示编译器应该优先选择哪个重载。 优先考虑优先级最高的超载。
此功能的目的是让库作者在添加新的重载时避免出现歧义。 库作者应谨慎使用该属性,以避免混淆。
field
关键字
field
上下文关键字在 C# 13 中作为预览特性提供。 令牌 field
访问属性访问器中的编译器合成支持字段。 它使你可以编写访问器的主体,而无需在类型声明中显式声明备份字段。 可以为字段支持的属性的一个或两个访问器声明一个主体。
field
功能作为预览功能发布。 我们希望从你的使用经验中学习。 如果类型中还包含一个名为 field
的字段,则在读取代码时可能会出现重大更改或混淆。 可以使用 @field
或 this.field
来消除 field
关键字和标识符之间的歧义。
重要
field
关键字是 C# 13 中的一项预览功能。 必须使用 .NET 9,并在项目文件中将 <LangVersion>
元素设置为 preview
,才能使用 field
上下文关键字。
在有名为 field
的字段的类中使用 field
关键字功能时应小心谨慎。 新的 field
关键字在属性访问器的范围内隐藏一个名为 field
的字段。 可以更改 field
变量的名称,或者使用 @
令牌将 field
标识引用为 @field
。 可以通过阅读 field
关键字的功能规范来了解详细信息。
如果试用了此功能并有反馈,请将其添加到 存储库中的 csharplang
。