可选参数和命名实参是C#4.0新增的特性。
本文将对可选参数和命名实参做个简单的介绍。之所以放一块来介绍,是因为两者经常成对出现,这样就可以在实例中将两者结合起来进行演示。
不过一开始,还是会分开对可选参数和命名实参进行说明。
1.可选参数
创建可选参数非常简单,只需在声明方法时将常量赋值给某个参数,那以后调用方法时就可以不用指定该参数了,若不指定该参数,则该参数将采用方法声明时指定的默认值,当然,你若不想采用指定的默认值的话,你还是可以像其他参数一样对其重新指定值的。在这里,被指定了默认值的参数即为可选参数。
下面给出的例子中,声明了一包含可选参数的方法并对其进行调用,代码如下:
private static void ParamsExp(int x, int y=9, int z=10){ const string SOME_SPACE=" ";//间隔两个空格 Console.Write(x); Console.Write(SOME_SPACE); Console.Write(y); Console.Write(SOME_SPACE); Console.Write(z);}static void Main(string[] args){ //给定所有参数 ParamsExp(0, 1, 2); //换行 Console.WriteLine(); //给定2个参数,省略1个参数 ParamsExp(0, 1); //换行 Console.WriteLine(); //给定1个参数,省略2个参数 ParamsExp(0); }
先来看看方法ParamsExp的代码,方法ParamsExp中的参数y及参数z为可选参数,y指定默认值9,z指定默认值10。即调用方法ParamsExp时,若未指定参数y即z,则y将使用默认值9,z将采用默认值10。
再来看看方法ParamsExp的调用代码,对于方法调用“ParamsExp(0, 1)”,没指定参数z,所以z将采用默认值10,最终方法返回值为“0 1 10”(中间含一空格分隔);对于方法调用“ParamsExp(0)”,参数y及z均没有被指定,所以均将采用默认值,最终方法返回值为“0 9 10”。而方法调用“ParamsExp(0, 1, 2)”三个参数都被指定,所以y及z的默认值均不会被使用,最终方法返回值为“0 1 2”。
下面给出程序的执行结果(如下),与我们的分析一致。
接下来,对前面的方法ParamsExp做一个小小的改动,代码如下:
private static void ParamsExp(int x=0, string y="y", string z="z"){ const string SOME_SPACE=" ";//间隔两个空格 Console.Write(x); Console.Write(SOME_SPACE); Console.Write(y); Console.Write(SOME_SPACE); Console.Write(z);}
并使用下面的代码来调用该函数:
static void Main(string[] args){ //省略第一个参数 ParamsExp("a", "b"); }
我们会发现此时将会出现编译错误,提示“与CanSelectedParams.Program.ParamsExp(int, string, string)最匹配的重载方法具有一些无效参数”。
那么我们到底改动了什么,竟然导致了编译错误。
首先,我们将ParamsExp方法的3个参数都改为了可选参数。其次,将后两个参数的类型改成了字符串类型,这仅仅是为了说明“ParamsExp("a", "b")”(方法调用)省略了第一个参数而已。其实,问题就出在我们省略的是第一个参数,而编译器并不能判断出我们省略了第一个参数,它会假设我们提供的实参顺序与参数定义的顺序一致。所以在这里,编译器还是认为我们省略的是第三个参数,那么调用这个方法时,第一个参数就会被指定一个字符串,而该方法不包含第一个参数可转换为字符串的重载,所以就会产生编译错误。
通过前面的分析,我们了解到,下面的调用都是可行的。
//给定所有参数 ParamsExp(1, "a", "b");//给定2个参数,省略1个参数ParamsExp(1, "a");//给定1个参数,省略2个参数ParamsExp(1);///不指定任何参数ParamsExp();
对于方法调用“ParamsExp()”,没有指定任何参数,所以3个参数都会采用默认值,所以该调用会返回“0 y z”。
通过前面的学习,了解到:所有可选参数必须出现在必备参数之后的。可选参数可以是任何类型,但他们的默认值必须为常量。
最后,探讨一下使用可选参数的好处。使用可选参数可以显著地降低重载的数量。要想说明这个其实很简单,前面的例子或许就是最好的说明。
假设现在有以下方法重载:
private static void ParamsExp(int x, int y, int z)private static void ParamsExp(int x, int y)private static void ParamsExp(int x)private static void ParamsExp()
若使用可选参数,使用下面的一行代码就可以搞定。
private static void ParamsExp(int x=0, int y=0, int z=0)
是不是很爽啊。
2.命名实参
命名实参指的是在指定实参的值时,可以同时指定相应参数的名称。这样的话,编译器就会判断参数的名称是否正确,并将指定的值赋给这个参数。
下面来看个简单的例子。
private static void NamedExp(int x, int y, int z){ const string SOME_SPACE = " ";//间隔两个空格 Console.Write(x); Console.Write(SOME_SPACE); Console.Write(y); Console.Write(SOME_SPACE); Console.Write(z);}static void Main(string[] args){ //正常的调用方法 NamedExp(0, 1, 2); //为所有实参指定名称 NamedExp(x: 0, y: 1, z: 2); NamedExp(y: 1, x: 0, z: 2); NamedExp(y: 1, z: 2, x: 0); //为部分实参指定名称 NamedExp(0, y: 1, z: 2); NamedExp(0, z: 2, y: 1); NamedExp(0, 1, z: 2);}
该实例演示了正常的方法调用,同时还演示了为所有参数指定名称和为部分参数指定名称的方法调用。
我们发现,使用命名实参后,参数的位置可以发生改变,如方法调用“NamedExp(x: 0, y: 1, z: 2)”,“NamedExp(y: 1, x: 0, z: 2)”及“NamedExp(y: 1, z: 2, x: 0)”三者参数的位置不一样,但是结果却是相同的。因为实参是按照参数的名称来匹配的,而不再是根据参数的位置来匹配。在混合使用了命名实参和普通实参的函数调用中,所有命名实参必须位于普通实参的后面。
加上换行代码后,执行程序,得到下面的结果。
3.好基友是要在一起的
先给出方法签名:
private static void ParamsExp(int x, int y=10, int z=20)
再给出调用代码:
ParamsExp(1,z:30)
在给出的两行实例代码中包含了我们今天研究的一切内容,下面一一道来。
一个必备参数:int x
两个可选参数:int y=10, int z=20
一个普通实参:1
一个命名实参:z:30
其实还有一个采用了默认值的实参。
是不是内容很丰富啊。
到这里,你肯定会问,将他们两者放一起有什么好处呢?
可以通过使用命名实参和可选实参来为可选形参省略实参。像“ParamsExp(1,z:30)”中就省略了参数y。当然这里的实例过于简单,好处似乎不是非常明显,但是如果想省略三四个实参而只指定最后一个,这时候好处就很明显了。
就写到这里了。总结也就免了,呵呵。