参数
可选参数与命名参数
可选参数,或者默认参数,参数是从左往右计算的。因此,函数定义时,如果有可选参数,那么可选参数必须在最右边。当调用函数时可以使用命名参数。
public static void Test(int a, string b, double c = 1.0);
我们可以这样调用Test(2, "3");Test("2", a:2);
调用函数时,使用的命名参数必须是最右边
可变参数
params修饰,比如public static void Test(params int[] arr);调用的时候使用Test(1,2,3);如果没有params参数修饰,那么需要Test(new int[]{1,2,3});但是要求params参数必须是最右边的一个,记住是一个,一个函数只能使用一个params。原理也不复杂,当你传入Test(1,2,3)的时候,系统会检测是否有函数的参数与其完全匹配,如果没有,就检查是否有params参数,如果有检查是否匹配,如果匹配,那么在上例中系统就会生成一个数组包含{1,2,3},然后作为参数放入函数参数中。因此使用params是好性能的,因为会生成一个数组。一般情况下,是使用重载函数。比如类库中的string类中的方法签名:
如果所有的重载函数都不匹配,那么就会检测是否匹配params,因为大部分的Concat操作都是2,3,4个Concat的,所以定义这些重载,而很少情况会调用params的重载函数。
函数传参
函数传参有两种情况,一种是值传递,一种是引用传递。很多人误认为void Test(string str)这样是引用传递,因为将实参str = "123"传入Test之后,进行一系列的操作,最后str的值会修改,因此误认为这样是引用传参。其实不是,C#中的引用传参,需要使用ref(有进有出)和out(只出不进)关键字来修饰的。而其他情况都是值传递,只不过如果形参是引用类型的话,那么它值传递时拷贝的副本是引用,即存在str和str1两个引用指向"123"这个对象,所以对str1进行操作也会修改"123"这个对象的值。大家可以通过这么一个程序来验证这个事实。
public void Test(string str)
{
str = null;
}
调用如下:
string str = "123";
Test(str);
Console.WriteLine(str); // 输出123
返回值和形参设计建议
形参设计的范围大一点,而返回值的范围小一点,比如
public FileStream Test<T>(IEnumable<T> collection);这样的设计更加的灵活,不易出错。而且考虑到了协变性和逆变性。