C# Params Collections 详解:比 params T[] 更强大的新语法

简介

Params CollectionsC# 12 中引入的新特性,它扩展了传统的 params 关键字功能,使其不仅支持数组,还能支持各种集合类型。这个特性使得方法能够接受可变数量的参数,并且这些参数可以自动转换为指定的集合类型。

关键特点:

  • 可变参数:调用者可以传递任意数量的参数(包括零个)。

  • 类型安全:params 参数是强类型的,编译器确保参数类型匹配。

  • 单一 params 参数:一个方法只能有一个 params 参数,且必须是最后一个参数。

  • C# 12 扩展:支持非数组集合类型(如 List<T>, Span<T>),适合高性能或特定场景。

核心特性

支持任意集合类型
  • 可指定 List<T>、Span<T>、IReadOnlyCollection<T> 等作为参数类型
csharp 复制代码
public void LogEntries(params List<string> messages) { ... }
自动集合构造
  • 编译器自动将离散参数转换为目标集合类型实例
csharp 复制代码
AnalyzeNumbers(10, 20, 30); 
// 等效于:
AnalyzeNumbers(new List<int> { 10, 20, 30 });
与现有 params 兼容
  • 传统 params T[] 仍然有效

  • 新语法不会破坏已有代码

传统 params 关键字

C# 12 之前,params 关键字只能用于数组:

csharp 复制代码
// 传统的 params 数组用法
public void ProcessNumbers(params int[] numbers)
{
    foreach (var number in numbers)
    {
        Console.WriteLine(number);
    }
}

// 调用方式
ProcessNumbers(1, 2, 3, 4, 5);

Params Collections 的新特性

C# 12 扩展了 params 关键字,使其能够用于任何集合类型,只要该类型满足特定条件。

基本语法
csharp 复制代码
// 使用 params 与集合类型
public void ProcessNumbers(params List<int> numbers)
{
    foreach (var number in numbers)
    {
        Console.WriteLine(number);
    }
}

// 调用方式不变
ProcessNumbers(1, 2, 3, 4, 5);

支持的条件

要使集合类型能够与 params 关键字一起使用,必须满足以下条件之一:

  • 集合类型必须有一个无参数的构造函数

  • 集合类型必须有一个 Add 方法,用于添加元素

  • 集合类型必须实现 IEnumerable<T>

自定义集合与 params
csharp 复制代码
// 自定义集合类
public class NumberCollection : IEnumerable<int>
{
    private readonly List<int> _numbers = new();
    
    public void Add(int number) => _numbers.Add(number);
    
    public IEnumerator<int> GetEnumerator() => _numbers.GetEnumerator();
    
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

// 使用自定义集合作为 params 参数
public void ProcessNumbers(params NumberCollection numbers)
{
    foreach (var number in numbers)
    {
        Console.WriteLine(number);
    }
}

// 调用
ProcessNumbers(1, 2, 3, 4, 5);

实际应用示例

与 Span 和 ReadOnlySpan 结合使用
csharp 复制代码
// 使用 Span 作为 params 参数
public void ProcessData(params Span<int> data)
{
    for (int i = 0; i < data.Length; i++)
    {
        data[i] *= 2;
    }
}

// 调用
int[] array = [1, 2, 3, 4, 5];
ProcessData(array);
Console.WriteLine(string.Join(", ", array)); // 输出: 2, 4, 6, 8, 10
与 Immutable Collections 结合使用
csharp 复制代码
using System.Collections.Immutable;

// 使用不可变集合作为 params 参数
public void ProcessItems(params ImmutableArray<string> items)
{
    foreach (var item in items)
    {
        Console.WriteLine(item);
    }
}

// 调用
ProcessItems("apple", "banana", "cherry");

高级用法

泛型方法与 params 集合
csharp 复制代码
// 泛型方法中使用 params 集合
public void ProcessCollection<T>(params List<T> collection)
    where T : notnull
{
    foreach (var item in collection)
    {
        Console.WriteLine(item);
    }
}

// 调用
ProcessCollection("a", "b", "c"); // 字符串列表
ProcessCollection(1, 2, 3);       // 整数列表
与模式匹配结合使用
csharp 复制代码
// 使用模式匹配处理 params 集合
public void HandleValues(params int[] values)
{
    switch (values)
    {
        case [var first, .. var middle, var last]:
            Console.WriteLine($"首: {first}, 尾: {last}, 中间有 {middle.Length} 个元素");
            break;
        case [var single]:
            Console.WriteLine($"单个值: {single}");
            break;
        case []:
            Console.WriteLine("空集合");
            break;
    }
}

// 调用
HandleValues(1, 2, 3, 4, 5); // 输出: 首: 1, 尾: 5, 中间有 3 个元素
HandleValues(42);            // 输出: 单个值: 42
HandleValues();              // 输出: 空集合
与接口结合使用
csharp 复制代码
// 使用接口作为 params 参数
public void ProcessEnumerables(params IEnumerable<int>[] collections)
{
    foreach (var collection in collections)
    {
        int sum = collection.Sum();
        Console.WriteLine($"集合总和: {sum}");
    }
}

// 调用
ProcessEnumerables(
    new List<int> { 1, 2, 3 },
    new int[] { 4, 5, 6 },
    new HashSet<int> { 7, 8, 9 }
);
高性能求和(使用 Span<T>
csharp 复制代码
public decimal Average(params Span<decimal> numbers)
{
    if (numbers.Length == 0) return 0;
    decimal sum = 0;
    foreach (var num in numbers)
    {
        sum += num;
    }
    return sum / numbers.Length;
}

Console.WriteLine(Average(1.5m, 2.5m, 3.5m)); // 输出: 2.5
  • 使用 Span<decimal> 避免数组分配,提高性能。

  • 适合处理大量数值计算。

适用场景

  • 简化方法调用:允许调用者传递任意数量的参数,减少重载需求。

  • 处理集合数据:适合处理列表、数组或序列,例如日志记录、字符串连接、数学计算。

  • 高性能场景(C# 12+):使用 Span<T>ReadOnlySpan<T> 减少堆分配,优化性能。

  • 与本机代码交互:Span<T> 类型的 params 参数适合传递连续内存块。

  • 灵活接口设计:为方法提供通用接口,支持不同数量的输入。

相关推荐
Zhen (Evan) Wang2 小时前
从客户端的HTTP 请求到后端 .NET 8 API的整个生命周期 - 处理请求和响应的主要方式
c#·.net
用户298698530143 小时前
如何在 C# 中创建、读取和更新 Excel 文档
后端·c#·excel
zybsjn3 小时前
多线程环境下 Dictionary 高 CPU 问题排查:一次真实的 .NET 线上事故分析
.net
总有刁民想爱朕ha3 小时前
.NET 8 AOT教程的使用
.net·.net8教程
Zhen (Evan) Wang4 小时前
从客户端的HTTP 请求到后端 .NET 8 API的整个生命周期
c#·.net
浅陌sss4 小时前
C#中记录一下使用字符串文本调用泛型方法
c#
SEO-狼术4 小时前
ASP.NET Zero v15.0.0 adds full .NET
后端·asp.net·.net
毕设源码-邱学长5 小时前
【开题答辩全过程】以 基于C#的超市管理系统为例,包含答辩的问题和答案
开发语言·c#
2501_930707785 小时前
如何使用C#代码将多张图片整合为一个PDF文档
开发语言·pdf·c#