params 参数必须是形参列表中最后一个且唯一的一维数组参数,用于支持调用时参数个数不确定的场景;不可与ref/out共用,不可重复声明,类型须明确且不支持多维或泛型T[]直接使用。

params 不是语法糖,也不是可选参数的替代品,它专用于解决“调用时参数个数不确定”这一具体问题——而且只在方法声明的末尾生效。
params 必须放在参数列表最后,且只能有一个
这是编译器强制的硬性规则。一旦违反,CS1736 错误直接报出:“params 参数必须是形参列表中的最后一个”。比如下面写法会失败:
public void Log(string level, params string[] messages, bool timestamp) // ❌ 编译不过
正确写法只能是:
public void Log(string level, bool timestamp, params string[] messages) // ✅
- 哪怕你只想传一个
string,messages也得放最后 - 不能和
ref、out同时修饰同一个参数 - 不能有两个
params int[] a, params string[] b—— 编译器只认一个
调用时既可传数组,也可传逗号分隔的值
params 的核心价值在于调用侧的灵活性:你不用提前构造数组,但底层统一按数组处理。
PrintNumbers(1, 2, 3); // ✅ 自动打包成 int[3]
int[] arr = {4, 5}; PrintNumbers(arr); // ✅ 显式传数组
PrintNumbers(); // ✅ 空调用,内部 length == 0
注意:null 是合法实参,但会导致运行时 NullReferenceException(除非你手动判空),而空数组 new int[0] 和无参数调用效果一致。
params 参数类型必须是一维数组,且元素类型明确
不能写 params int[][](二维数组)、params object(非数组)或 params List。常见错误包括:
- 误以为
params dynamic[]能接收任意类型混搭 —— 实际上每个元素仍被当作dynamic,不是类型擦除 - 用
params object[]接收结构体(如int)会触发装箱,有性能开销 - 泛型方法里不能直接写
params T[](C# 不支持泛型约束绑定到params),得靠重载或额外类型参数绕过
和普通可选参数、方法重载的边界容易混淆
很多人想用 params 替代一堆重载,但要注意:它不解决“不同参数类型组合”的问题。例如:
void Process(params string[] s) // 只能接 string 列表
void Process(params int[] i) // ❌ 无法共存:签名冲突
如果你需要同时支持 string 和 int,要么用 params object[](失去类型安全),要么保留多个重载,或者改用 params Span(.NET 5+,需 unsafe 上下文或额外约束)。
最常被忽略的一点:params 数组在方法体内是引用传递的,但如果你在方法里把它重新赋值(比如 args = new string[0]),外部原数组不受影响——因为参数本身是按值传递的数组引用,不是 ref string[]。










