C# 语法书 之可选参数

适用C#版本:4.0以后

首先给某些语言狂热分子泼点冷水,可选参数的概念来自Visul Basic而不是Ruby。在C# 2.0年代,并没有可选参数这个概念。 这个限制是因为在 C# 中,任何地方都引入面向对象思想,所以尽量使用重载而不是可选参数。


class MsgEcho
{
public MsgEcho():this("hi,world")
{
}
public MsgEcho(String name)
{
Console.WriteLine(name);
}
}

可选参数(Optional parameters)是C# 4.0的一个新特性,可以为一个方法的参数设置一个默认值。为一个参数设置一个默认值,这个参数就是可选参数,一旦被调用的方法忽略可选参数,就会用默认值代替。

为什么需要开放命名参数和可选参数呢?这是出于动态语言运行时兼容性的要求。动态语言中存在动态绑定的参数列表,有时候并不是所有的参数值都需要指定(有些语言可能没有重载决策);另外,在一些 COM 互操作时,往往 COM Invoke 的方法参数列表非常的长(例如 ExcelApplication.Save,可能需要 12 个参数),但 COM 暴露的参数的实际值往往为 null,只有很少一部分参数需要指定植,如 ExcelApplication.Save(),可能不需要指定任何参数值,或者仅仅一个值,例如 fileType

合 Visual Basic、Visual C++ 或者 Delphi 一样,C# 声明可选参数的方法就是在方法参数声明后面加上参数的默认值:


class MsgEcho
{
public MsgEcho(String name="hi,world")
{
Console.WriteLine(name);
}
}


代码中的name = ”hi,world“是可选参数。作用是传递一个“hi,world”字符串作为选用参数。使用可选参数有一些限制:

1. 可选参数必须从右往左出现在参数列表中(必须后出现),可选参数右边的参数(如果有的话)必须是可选参数。这些声明是非法的:

[code]
void Foo1(int a = 0, int b);
[/code]

2.可选参数的初始值必须是一个在编译期可确定的常量,如果是除String之外的引用类型(包括那个特殊的dynamic类型),默认值只能是null。这些声明是非法的:

[code]
void Foo1(string name=String.Empty);
void Foo2(DateTime time=DateTime.Now);
[/code]

3. 可选参数不可用ref,out等修饰符。


4. 调用带有多个可选参数的方法时,不可以置空以省略中间的某些参数,这样的调用时非法的:
[code]
Add(1,,3);//错误语法
[/code]
而应该使用命名参数来调用该方法
[code]
Add(Param1:1,Param3:3);
[/code]

面向对象的一个主要特征是:当调用对象上的某个方法的时候,由对象自己在运行时通过查找与对象关联的一张表来决定执行哪些代码,这被称为"dynamic dispatch",因此在对象中,可以有同名不同参数表的函数,即我们说的重载。

毫无疑问,命名参数和可选参数让CLR在方法的重载决策(overload resolution)变得稍微复杂了一些,因此在某些情况下,调用哪些代码是由重载决策来决定的:

1. 在带可选参数的方法签名中,重载决策不会认可被可选参数代替的重载版本:
[code]
public void Foo(int a, String s = "i'm a string", dynamic b = null, MyClass c = null);
public void Foo(int a, String s = "i'm a string");
[/code]

如果按照以下方式调用,编译器会提示你它已经被上面两个方法confused了:
[code]
Foo(2);
Foo(a:2);
[/code]

2. 在调用方式同样合法的情况下,重载决策会优先选择不带可选参数的重载版本。比如下面两个方法:
[code]
public void Foo(int a, String s = "i'm a string", dynamic b = null, MyClass c = null);
public void Foo(int a);
[/code]

如果使用Foo(2)调用方法:被调用的会是void Foo(int a)这个版本:

3.同型的可选参数,如果不用命名参数赋值,则参数按从左到右的顺序被赋值
[code]
public void Foo(int a=0,int b=0);
[/code]

如果使用Foo(2)调用方法,则a=2,b=0,可能与调用者的预期是不同的。

4.在调用方式同样合法的情况下,重载决策会优先选择类型最为匹配(最易转化)的重载,例如下面两个方法:
[code]
public void Foo(byte a, String s = "i'm a string", dynamic b = null, MyClass c = null);
public void Foo(object a);
[/code]
Foo(2)和Foo(a:2)都将调用前一个方法,因为int到byte是值类型之间的转化,其代价要比从int转到object的代价低。


策略

在下面情况下,你应该考虑使用使用重载:


The logic in the procedure code is significantly different depending on whether the calling code supplies an optional argument or not.

The procedure code cannot reliably test whether the calling code has supplied an optional argument. This is the case, for example, if there is no possible candidate for a default value that the calling code could not be expected to supply.

在下面情况,你可以考虑使用一个或多个可选参数:

The only required action when the calling code does not supply an optional argument is to set the parameter to a default value. In such a case, the procedure code can be less complicated if you define a single version with one or more Optional parameters.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值