作者:Kathleen Dollard - 首席项目经理
排版:Alan Wang
C# 13 提供的功能让您能够更轻松、更安全、更快速地以您熟悉和喜爱的风格编写代码。您可以在 C# 13 中的新增功能中找到 C# 13 功能的完整列表。
C# 13 实现了一项长期以来的功能请求,现在允许集合表达式支持的任何集合类型作为params参数,而不仅仅是数组。 此功能基于 C# 12 中引入的集合表达式功能。
使用集合表达式奠定基础
C# 12 中引入了集合表达式,为以前创建各种集合的无数种方法提供了一种替代方法。在方法上下文中,这允许您在许多场景中简化集合的使用,包括调用方法:
// C# 12 之前
WriteByteArray(new[] { (byte)1, (byte)2, (byte)3 });
WriteByteSpan(stackalloc[] { (byte)1, (byte)2, (byte)3 });
// C# 12 之后
WriteByteArray([1, 2, 3]);
WriteByteSpan([1, 2, 3]);
static void WriteByteArray(byte[] bytes) { }
static void WriteByteSpan(Span<byte> bytes) { }
这也使我们能够统一 C# 处理集合的方式。请注意,集合表达式可以推断集合的类型和成员的类型。
params 数组
params 自 C# 1.0 以来就一直存在于该语言中。它允许调用代码以逗号分隔的列表形式包含零到多个参数。该方法以数组形式接收此列表,该列表可能为空:
WriteByteArray(1, 2, 3);
WriteByteArray();
static void WriteByteArray(params byte[] bytes) { }
在 C# 13 之前,必须在方法的参数列表中将 params 声明为数组。
除了使用值列表进行调用外,您还可以使用数组调用带有 params 参数的方法。 并且从 C# 12 开始,它可以是集合表达式:
WriteByteArray(1, 2, 3);
WriteByteArray([1, 2, 3]);
byte[] bytes = [4, 5];
WriteByteArray([1, 2, 3, .. bytes]);
static void WriteByteArray(params byte[] bytes) { }
params 集合
从 C# 13 开始,params 可以是任何支持集合表达式的集合类型:
WriteByteSpan(1, 2, 3);
WriteByteSpan();
static void WriteByteSpan(params Span<byte> bytes) { }
虽然这似乎是一个很小的变化,但它通常可以让编译器优化您的代码。例如,如果该方法使用 params Span,则编译器可以使用堆栈空间为该方法创建Span。这比分配数组的性能更好。
使用特定类型还可以向调用者传达集合的使用方式。例如,params IReadonlyList表示集合不会被修改。
如果您使用 params IEnumerable,则用户可以传递文字值列表、数组、List、任何实现 IEnumerable的集合类型或 LINQ 表达式:
WriteByteArray(1, 2, 3);
byte[] bytes = [1, 2, 3, 4, 5];
WriteByteArray(bytes.Where(x => x < 4));
static void WriteByteArray(params IEnumerable<byte> bytes) { }
当 params 接收器是接口并且参数是元素的离散列表或集合表达式时,编译器将使用具体类型。许多集合接口都有一个逻辑上合理的默认实现,例如 IReadonlyList对应ReadonlyCollection。由于 IEnumerable没有如此明显的选择,并且对 .NET 至关重要,因此它对 param 和集合表达式都使用了一种特殊的高性能类型。
重载
C# 支持重载方法 - 这意味着如果参数类型不同,则可以存在多个同名方法。您可以像重载其他参数一样对 params 集合进行重载。
例如,您可能在同一个解析范围内有一个带有 params IEnumerable的重载和一个带有 params ReadOnlySpan的重载。如果您传递一个值列表,则将选择 params ReadOnlySpan重载。如果您传递一个数组,则也将选择 params ReadOnlySpan重载,因为从数组到 ReadOnlySpan存在隐式转换。如果您传递 List,则将使用 params IEnumerable 重载:
public class ParamCollections
{public static void Overloads(){WriteNumbers(1, 2, 3);WriteNumbers([1, 2, 3]);WriteNumbers(new[] { 1, 2, 3 });byte[] ints = [1, 2, 3, 4, 5];WriteNumbers(ints.Where(x => x < 4));}// 此代码从静态本地函数转变为私有函数 // 因为局部函数不允许重载。 private static void WriteNumbers<T>(params IEnumerable<T> values) => Console.WriteLine("IEnumerable");private static void WriteNumbers<T>(params ReadOnlySpan<T> values) => Console.WriteLine("Span");
}
// 结果是:
//
// Span
// Span
// Span
// IEnumerable
编译器会为您挑选一个合理的重载。如果 Span 可用,它通常会被优先选择,因为这样可以避免在方法调用中进行内存分配。
无论您是否将 params 集合添加到您自己的代码中,它们都会使您的应用程序运行得更快,因为 .NET 运行时库现在可以在更多地方使用 Span 等高性能类型。您可以使用与 params 相同的概念,调用者在如何调用方法方面具有更大的灵活性,并且编译器将选择重载。
考虑重载
重载是 C# 的一个非常强大的功能。但与许多强大的功能一样,正确使用它非常重要。如果有多个同名的方法,它们应该执行相同的操作。它们在执行方式或性能上可能有所不同,但更改传递的类型不会给您的应用程序带来重大更改。通常,这意味着在调整参数值后调用单个实现。
如上例所示,调用代码的微小变化可能会导致调用不同的重载。对于所有 C# 应用程序来说都是如此,无论您是否使用 params 集合。
总结
我们对 C# 13 中的参数集合感到非常兴奋,迫不及待地想听听您的想法!
您可以在 C# 13 中的新增功能中了解 C# 13 中的所有功能。