命名参数
例如 卖家姓名、订单号和产品名称 的函数。
//PrintOrderDetails(string sellerName, int orderNum, string productName);
PrintOrderDetails("Gift Shop", 31, "Red Mug");
如果不记得形参的顺序,但却知道其名称,则可以按任意顺序发送实参。
PrintOrderDetails(orderNum: 31, productName: "Red Mug", sellerName: "Gift Shop");
PrintOrderDetails(productName: "Red Mug", sellerName: "Gift Shop", orderNum: 31);
命名实参还可以标识每个实参所表示的含义,从而改进代码的可读性。
可选参数
方法、构造函数、索引器或委托的定义可以指定其形参为必需还是可选。 任何调用都必须为所有必需的形参提供实参,但可以为可选的形参省略实参。
每个可选形参都有一个默认值作为其定义的一部分。 如果没有为该形参发送实参,则使用默认值。 默认值必须是以下类型的表达式之一:
- 常量表达式;
new ValType()
形式的表达式,其中ValType
是值类型,例如 enum 或 struct;- default(ValType) 形式的表达式,其中
ValType
是值类型。
并且可选参数定义于参数列表的末尾和必需参数之后。
public void ExampleMethod(int required, string optionalstr = "default string",
int optionalint = 10)
如果调用方为一系列可选形参中的任意一个形参提供了实参,则它必须为前面的所有可选形参提供实参。 实参列表中不支持使用逗号分隔的间隔。
例如:下面对 ExampleMethod
的调用会导致编译器错误,原因是为第三个形参而不是为第二个形参提供了实参
//错误做法
ExampleMethod(3, ,4);
//正确做法
ExampleMethod(3, optionalint: 4);
注意事项
- 不要重命名参数变量。否则任何调用者以命名参数的方式传递实参,他们的代码也必须修改。例如:在前面PrintOrderDetails方法声明中,将orderNum变量重命名未orderID,后续对PrintOrderDetails的两个调用就会造成编译器显示一下消息:error CS1739:"PrintOrderDetails"的最佳重载没有名为"orderNum"的参数。
- 如果参数用ref或out关键字进行了标识,就不能设置默认值。因为没有办法为这些参数传递有意义的默认值。
- 如果PrintOrderDetails方法从外部模块调用,更改参数的默认值具有潜在的危险性。call site(发出调用的地方,可理解成调用了一个目标方法的表达式或代码行)在它的调用中嵌入默认值。如果以后更改了参数的默认值,单没有重新编译包好call site的代码,它在调用方法PrintOrderDetails时仍然会传递旧的默认值。可考虑将默认值0/null作为哨兵值使用,从而之处默认行为,这样一来,即使更改了默认值,也不必重新编译包含了call site的全部代码。
例如:
//不要这样做
private string MakePath(string filename = "Untitled")
{
return String.Format(@"C:\{0}.txt", filename);
}
//应该这样做
private string MakePath(string filename = "Untitled")
{
//“??” 空接合操作符
return String.Format(@"C:\{0}.txt", filename ?? "Umtitled");
}