用 params
修饰符声明的参数是参数数组。如果形参表包含一个参数数组,则该参数数组必须位于该列表的最后而且它必须是一维数组类型。例如,类型 string[]
和 string[][]
可用作参数数组的类型,但是类型 string[,]
不能。不可能将 params
修饰符与 ref
和 out
修饰符组合起来使用。
在一个方法调用中,允许以下列两种方式之一来为参数数组指定对应的参数:
- 赋予参数数组的参数可以是一个表达式,它的类型可以隐式转换为该参数数组的类型。在此情况下,参数数组的作用与值参数完全一样。
- 或者,此调用可以为参数数组指定零个或多个参数,其中每个参数都是一个表达式,它的类型可隐式转换为该参数数组的元素的类型。在此情况下,此调用创建一个长度对应于参数个数、类型与该参数数组的类型相同的一个数组实例,并用给定的参数值初始化该数组实例的元素,并将新创建的数组实例用作实参。
除了允许在调用中使用可变数量的参数,参数数组与同一类型的值参数完全等效。
示例
1 using System; 2 3 class Test 4 5 { 6 7 static void F(params int[] args) 8 9 { 10 11 Console.Write("Array contains {0} elements:", args.Length); 12 13 foreach (int i in args) 14 15 Console.Write(" {0}", i); 16 17 Console.WriteLine(); 18 19 } 20 21 static void Main() 22 23 { 24 25 int[] arr = {1, 2, 3}; 26 27 F(arr); 28 29 F(10, 20, 30, 40); 30 31 F(); 32 33 } 34 35 }
产生输出
Array contains 3 elements: 1 2 3
Array contains 4 elements: 10 20 30 40
Array contains 0 elements:
F
的第一次调用只是将数组 arr
作为值参数传递。F
的第二次调用自动创建一个具有给定元素值的四元素 int[]
并将该数组实例作为值参数传递。与此类似,F
的第三次调用创建一个零元素的 int[]
并将该实例作为值参数传递。第二次和第三次调用完全等效于编写下列代码:
F(new int[] {10, 20, 30, 40});
F(new int[] {});
执行重载决策时,具有参数数组的方法可能以它的正常形式或以它的扩展形式成为适用的。只有在方法的正常形式不适用,并且在同一类型中尚未声明与方法的扩展形式具有相同签名的方法时,上述的方法扩展形式才可供选用。
示例
using System;
class Test
{
static void F(params object[] a)
{
Console.WriteLine("F(object[])");
}
static void F()
{
Console.WriteLine("F()");
}
static void F(object a0, object a1)
{
Console.WriteLine("F(object,object)");
}
static void Main()
{
F();
F(1);
F(1, 2);
F(1, 2, 3);
F(1, 2, 3, 4);
}
}
产生输出
F();
F(object[]);
F(object,object);
F(object[]);
F(object[]);
在该示例中,在同一个类中,已经声明了两个常规方法,它们的签名与具有参数数组的那个方法的扩展形式相同。因此,在执行重载决策时不考虑这些扩展形式,因而第一次和第三次方法调用将选择常规方法。当在某个类中声明了一个具有参数数组的方法时,同时再声明一些与该方法的扩展形式具有相同的签名的常规方法,这种情况比较常见。这样做可以避免为数组配置内存空间(若调用具有参数数组的方法的扩展形式,则无法避免)。
当参数数组的类型为 object[]
时,在方法的正常形式和单个 object
参数的扩展形式之间将产生潜在的多义性。产生此多义性的原因是 object[]
本身可隐式转换为 object
。然而,此多义性并不会造成任何问题,这是因为可以在需要时通过插入一个强制转换来解决它。
示例
using System;
class Test
{
static void F(params object[] args)
{
foreach (object o in args)
{
Console.Write(o.GetType().FullName);
Console.Write(" ");
}
Console.WriteLine();
}
static void Main()
{
object[] a = {1, "Hello", 123.456};
object o = a;
F(a);
F((object)a);
F(o);
F((object[])o);
}
}
产生输出
System.Int32
System.String
System.Double
System.Object[]
System.Object[]
System.Int32
System.String
System.Double
在 F
的第一次和最后一次调用中,F
的正常形式是适用的,这是因为存在一个从自变量类型到参数类型的转换(这里,其实两者都是 object[]
类型)。因此,重载决策选择 F
的正常形式,而且将该参数作为常规的值参数传递。在第二次和第三次调用中,F
的正常形式不适用,这是因为不存在从自变量类型到参数类型的转换(类型 object
不能隐式转换为类型 object[]
)。但是,F
的扩展形式是适用的,因此重载决策选择它。因此,这两个调用都创建了一个具有单个元素的、类型为 object[]
的数组,并且用给定的参数值(它本身是对一个 object[]
的引用)初始化该数组的唯一元素。