目录
9.1 可选参数和命名参数
9.2 隐式类型的局部变量
9.3 以传引用的方式向方法传递参数
9.4 向方法传递可变数量的参数
9.5 参数和返回类型的设计规范
9.6 常量性
9.1 可选参数和命名参数
设计方法的参数时,可为部分或全部参数分配默认值。然后,调用这些方法的代码可以选择不提供部分实参,使用其默认值。此外,调用方法时可通过指定参数名称来传递实参。
9.1.1 规则和原则
如果在方法中为部分参数指定了默认值,请注意一下附加的规则和原则
可为方法,构造器方法和有参属性(C#索引器)的参数指定默认值。还可为属于委托定义一部分的参数指定默认值。以后调用该委托类型的变量时可省略实参来接受默认值。
有默认值的参数必须放在没有默认值的所有参数之后。“参数数组”这种参数必须放在所有参数(包括有默认值的参数)之后,而且数组本身不能有一个默认值。
默认值必须是编译时能确定的常量值。包括基元类型,枚举类型以及能设为null的任何引用类型。值类型的参数可将默认值设为值类型的实例,并让它的所有字段都包含零值(可用default关键字和new关键字)。
不要重命名参数变量,否则任何调用者以传参数名的方式传递实参,它们的代码也必须修改。
如果方法从模块外部调用,更改参数的默认值具有潜在的危险性。
如果参数用ref或out关键字进行了标识,就不能设置默认值。因为没有办法为这些参数传递有意义的默认值。
使用可选或命名参数调用方法时,还要注意以下附加的规则和原则
实参可按任意顺序传递,但命名实参只能出现在实参列表的尾部。
可按名称将实参传给没有默认值的实参,但所有必须的实参都必须传递。
C#不允许省略逗号之间的实参。
9.1.2 DefaultParameterValueAttribute和OptionalAttribute
在C#中,一旦为参数分配了默认值,编译器就会在内部向该参数应用定制特性System.Runtime.InteropServices.OptionalAttribute。该特性会在最终生成的元数据中持久性地存储下来。此外,编译器向参数应用System.Runtime.InteropServices.DefaultParameterValueAttribute特性,并将该属性持久性存储到生成的文件的元数据中。然后,会向DefaultParameterValueAttribute的构造器传递你在源代码中指定常量值。之后,一旦编译器发现某个方法调用缺失了部分实参,就可以确定省略的是可选的实参,并从元数据中提取默认值,将值自动嵌入调用中。
9.2 隐式类型的局部变量
C#能根据初始化表达式的类型推断方法中的局部变量的类型。
不能用var声明方法的参数类型。因为编译器必须根据在call site 传递的实参来推断参数类型,但 call site 可能一个都没有,也可能有好多个。
不能用var声明类型中的字段。因为字段可以被多个方法访问,而C#团队认为这个协定(变量的类型)应该显示陈述。另一个原因是一旦允许,匿名类型就会泄露到方法的外部。
9.3 以传引用的方式向方法传递参数
CLR默认所有方法参数都传值。传递引用类型的对象时,对象引用(或者说指向对象的指针)被传给方法。注意引用(或指针)本身是传值的,意味着方法能修改对象,而调用者恩能看到这些修改。对于值类型的实例,传给方法的是实例的一个副本,意味着方法将获得它专用的一个值类型实例副本,调用者中的实例不受影响。
CLR允许以传引用而非传值的方式传递参数。C#用关键字out或ref支持这个功能。两个关键字都告诉编译器生成元数据来指明该参数是传引用的。编译器将生成代码来传递参数的地址,而非传递参数本身。
为值类型使用out和ref,效果等同于以传值的方式传递引用类型。对于值类型,允许方法操纵单一的值类型实例。调用者必须为实例分配内存,被调用者则操纵该内存(中的内容)。对于引用类型,调用代码为一个指针分配内存,被调用者则操纵这个指针。所以仅当方法“返回”对“方法知道的一个对象”的引用时,为引用类型使用out和ref才有意义。
9.4 向方法传递可变数量的参数
使用params关键字,关键字只能应用于方法签名中的最后一个参数。它会告诉编译器向参数应用定制特性System.ParamArrayAttribute的一个实例。
9.5 参数和返回类型的设计规范
声明方法的参数类型时,应尽量指定最弱的类型,宁愿要接口也不要基类。
方法的返回类型最好声明为最强的类型(防止受限于特性类型)。
如果想保持一定的灵活性,在将来更改方法返回的东西,请选择一个较弱的返回类型。
9.6 常量性
不支持