C# 13 正逐步成型,其特性聚焦于灵活性、性能以及让日常使用的功能更加出色。在今年的 Microsoft Build 大会上,展示了 C# 13 即将迎来的一些变化。今天,我想分享 C# 13 中当前可尝试的功能状态,并提供关于此版本及未来规划的特性更新。让我们详细看看这些新特性吧。
params
集合增强,提升灵活性lock
对象- 索引器改进
- 转义序列
\e
- 部分属性
- 方法组自然类型改进
- 在
async
方法和迭代器中使用ref
和unsafe
- 扩展类型(Extension Types)更新
如何立即体验 C# 13
在深入了解 C# 13 的每个新特性之前,你可能想知道如何尝试它们。你可以在.NET 9 预览版(撰写时为 Preview 6)和 Visual Studio 2022-17.11 的最新预览版中找到 C# 13 的最新预览。要访问预览特性,请在项目文件中将语言版本设置为 preview
:
<LangVersion>preview</LangVersion>
params
集合
C# 13 扩展了 params
关键字,使其能与任何可通过集合表达式构造的类型配合使用。这无论是在编写方法还是调用方法时都增加了灵活性。
当 params
关键字出现在参数前,调用该方法时可以提供零个或多个值的逗号分隔列表。以下示例在所有版本的 C# 中都有效:
public void WriteNames(params string[] names) => Console.WriteLine(string.Join(", ", names));
WriteNames("Mads", "Dustin", "Kathleen");
WriteNames(new[] { "Mads", "Dustin", "Kathleen" }); // 输出均为:Mads, Dustin, Kathleen
从 C# 13 开始,params
参数可以是任何支持集合表达式的类型。例如:
public void WriteNames(params List<string> names) => Console.WriteLine(string.Join(", ", names));
当调用具有IEnumerable
参数的方法时,你可以传递 LINQ 表达式的结果。如果IEnumerable
参数有params
修饰符,也可以传递逗号分隔的列表。当你需要时,可以使用逗号分隔的列表,而需要时则使用 LINQ 表达式:
public void WriteNames(params IEnumerable<string> names) => Console.WriteLine(string.Join(", ", names));
重载解析
在编写方法时,你可以提供多个 params
重载。例如,添加一个 IEnumerable
重载以支持 LINQ,添加一个 ReadOnlySpan
或 Span
重载以减少分配,从而提高性能。
lock
对象
.NET 9 引入了新的 System.Threading.Lock
类型,用于互斥,相比锁定任意 System.Object
实例可能更高效。System.Threading.Lock
类型提案中有更多关于这个类型及其创建原因的信息。随着时间推移,这种类型预计将成为 C#代码中大多数锁定的主要机制。
C# 13 简化了使用这种类型的方式。当编译器识别到 lock
语句的目标是一个 System.Threading.Lock
对象时,C#现在生成对 System.Threading.Lock
API 的调用,并在可能错误地将 Lock
实例视为普通 object
时提供警告。
索引器从末尾初始化
索引运算符 ^
允许你相对于集合末尾来指示集合中的位置。这现在在初始化器中也可用。
class Program {
static void Main() {
var x = new Numbers {
Values = { [1] = 111, [^1] = 999 } // C# 13起有效
};
// x.Values[1] 是 111
// x.Values[9] 是 999,因为它是最后一个元素
}
}
转义序列 \e
C# 13 引入了一个新的转义序列,用于表示你所知的 ESCAPE
或 ESC
字符。之前必须将其键入为 \u001b
的变体。这个新序列在与使用 VT100/ANSI 转义码的终端交互时特别方便,用于 System.Console
。例如:
// C# 13之前
Console.WriteLine("\u001b[1mThis is a bold text\u001b[0m");
// 使用C# 13
Console.WriteLine("\e[1mThis is a bold text\e[0m");
部分属性
C# 13 新增了部分属性(partial properties)。和部分方法一样,它们的主要目的是支持源生成器。部分方法已存在多版本,并在 C# 9 中进行了额外改进。部分属性与它们的部分方法类似。
方法组自然类型改进
自然类型是指编译器确定的表达式类型,比如当类型被赋给 var
或 Delegate
时。如果是简单类型,这很简单。C# 10 中添加了对方法组的支持。当作为委托包含不带括号的方法名时,会使用方法组:
Todo GetTodo() => new(Id: 0, Name: "Name");
var f = GetTodo; // f 的类型为 Func<Todo>
C# 13 细化了确定自然类型的规则,以便按作用域考虑候选者,并修剪没有成功机会的候选者。更新这些规则意味着在处理方法组时更少的编译器错误。
允许 ref struct
C# 13 添加了一种指定泛型类型参数能力的新方法。默认情况下,类型参数不能是 ref struct
。C# 13 让你可以指定类型参数可以是 ref struct
,并应用相应的规则。虽然其他泛型约束限制了可以用作类型参数的类型集,但这个新规范扩大了允许的类型。我们将其视为一个反约束,因为它消除了而不是增加了限制。在 where
子句中使用 allows ref struct
语法,其中 allows
表示此用途的扩展:
T Identity<T>(T p) where T : allows ref struct => p;
// 正确
Span<int> local = Identity(new Span<int>(new int[10]));
在 async
方法和迭代器中使用 ref
和 unsafe
在 C# 13 之前,迭代器方法(使用 yield return
的方法)和 async
方法不能声明局部 ref
变量,也不能有 unsafe
上下文。
在 C# 13 中,async
方法可以声明 ref
局部变量,或 ref struct
类型的局部变量。这些变量不能跨 await
边界或 yield return
边界保存。
同样,C# 13 允许在迭代器方法中使用 unsafe
上下文。然而,所有 yield return
和 await
语句必须处于安全上下文中。这些放宽的限制使你能在更多地方使用 ref
局部变量和 ref struct
类型。
扩展类型更新
在.NET Build 公告博客文章中描述了扩展类型。当时,目标是让该功能的关键部分在 C# 13 中实现,但设计和实施需要更多时间。请期待在 C# 14(.NET 10)的早期预览中看到扩展类型。
总结
你可以在 [C# 13 新特性概览] 中找到更多关于这些特性的信息。你可以在 Roslyn 特性状态页面查看进展。同时,请务必关注.NET 9 预览版发行说明,你可以在那里找到每个发行版的 C# 发行说明。
Comments