c#中,关于参数有 ref,out,params等修饰关键字,同时,也允许参数具有默认值,通过参数名称进行调用。
如果一个方法有多个参数,并且有的参数具有默认值,那么具有默认值的参数应当在最后【也即,具有默认的参数右边不能有没有默认值的参数存在——–可变参数数组是个例外】。
类似:
public void func(int i,j = 5,k = 10);
对于func的调用:
func(0);//只是对i赋值,j取默认值5,k取默认值10
func(1,2);//对i,j分别赋值为i = 1,j = 2,k取默认值10
func(1,2,3);//三者分别赋值
还可以使用参数名进行赋值【从而允许乱序】
func(0,k:50);//i = 0,k = 50,j取默认值5
对于有默认值的参数,编译器在发现某个参数有默认值时,会为其加上DefaultParamValue和Optional特性【System.Runtime.InteropService命名空间】进行标注。
要注意的一点是:
有默认值的参数,值必须是编译时期可以确定的常量【值类型虽然用new但是是个例外,详情可以参看上篇介绍】
可变参数数组,也就是使用params关键字来修饰的数组参数。
首先引入例子:
public int add(int[] a){
//省略检查有效性
int sum = 0;
foreach(var i in a){
sum += i;
}
}
//调用端:
add(new int[]{1,2,3});
我如果希望这样写呢:
add(1,2,3);
此时就需要对add的参数进行params修饰了。
为了完整,接下来说说,ref和out修饰符。这两个修饰符本质区别是: 由【调用者/被调用者】来初始化传递参数。
ref:ref修饰的参数,要求调用者这边必须初始化,被调用者可以对参数进行读取和写入。
out:out修饰的参数,被调用者这边不需要初始化【也没有必要和意义】,被调用者不允许对参数进行读取【这就是为什么调用者初始化没有意义的原因】,并且必须对参数进行写入。
这里对于值类型通过ref,out修饰,不做过多说明【其实类似c++语言中的指针,进行传地址(地址传值)传递】。说说对于引用类型的修饰,同时这个例子还有一点其他需要注意的地方。
//首先定义了一个swap函数
public void swap(ref Object lhs,ref Object rhs){
Object o = lhs;
lhs = rhs;
rhs = o;
}
//接下来
string s1 = "hello";
string s2 = "world";
//Main
swap(ref s1,ref s2);
有问题吗?这段代码是无法通过编译的。编译器抱怨:swap(ref object, ref object)”最匹配的重载方法具有一些无效参数。什么意思?编译器要求对于这种方式,必须使用完全一致的类型。为什么要这样子呢?看个例子:
public void func(ref object o){
...
o = new List<string>();
}
//Main
string s = "xiaolu";
func(ref s);//ooh...
问题很明显了,感谢编译器不允许这样的代码通过。否则,s明明是个string,结果到了func中,转为了object,然后…变成了List<string>
…
要想让上面的代码可以运行,可以这样做【你也可以将swap的参数类型改成string,从而精确匹配了。这里不再描述】:
public void swap(ref object lhs,ref object rhs)
{
object o = lhs;
lhs = rhs;
rhs = o;
}
public static void Main(String[] args)
{
string s1 = "hello";
string s2 = "world";
object o1 = s1;
object o2 = s2;
new Demo().swap(ref o1, ref o2);
s1 = o1 as string;
s2 = o2 as string;
System.Console.WriteLine("s1:{0},s2:{1}", s1, s2);
}
或者认为转型太过麻烦,可以使用泛型方式:
public void swap<TELE>(ref TELE lhs,ref TELE rhs)
{
TELE o = lhs;
lhs = rhs;
rhs = o;
}
public static void Main(String[] args)
{
string s1 = "hello";
string s2 = "world";
new Demo().swap(ref s1, ref s2);
System.Console.WriteLine("s1:{0},s2:{1}", s1, s2);
}
个人更喜欢这个- - 方便。
其实,对于ref和out不管作用于引用还是值类型,简单可以认为,只要有这两个修饰符的修饰,就是一次取地址。这样理解就应该没有什么疑惑的地方【对于ref string,可以认为传递的是指针的指针,从而可以进行交换。达到我们的想要效果,因为我觉得这里还是传值,如果是可以传址(c++中的引用),那么就不需要ref或者out修饰,直接可以修改地址,而这里显然不行】
public void swap<TELE>( TELE lhs, TELE rhs)
{
TELE o = lhs;
lhs = rhs;
rhs = o;
System.Console.WriteLine("s1:{0},s2:{1}", lhs, rhs);//这里修改了,但是对于Main中没有影响,显然这里是一个地址的拷贝而已。
}
public static void Main(String[] args)
{
string s1 = "hello";
string s2 = "world";
new Demo().swap( s1, s2);
System.Console.WriteLine("s1:{0},s2:{1}", s1, s2);
}