在 C# 2.0 时代,当我们希望用类似于 C++ 的可选参数为参数指定默认值时,会得到一个编译器错误,指示“不允许参数的默认值”。这个限制是因为在 C# 中,任何地方都引入面向对象思想,所以尽量使用重载而不是可选参数。但在 C# 4.0 中这一点得到了一些改变。
为什么需要开放命名参数和可选参数呢?这也是出于动态语言运行时兼容性的要求。动态语言中存在动态绑定的参数列表,有时候并不是所有的参数值都需要指定(有些语言可能没有重载决策);另外,在一些 COM 互操作时,往往 COM Invoke 的方法参数列表非常的长(例如 ExcelApplication.Save,可能需要 12 个参数),但 COM 暴露的参数的实际值往往为 null,只有很少一部分参数需要指定植,如 ExcelApplication.Save(),可能不需要指定任何参数值,或者仅仅一个值,例如 fileType。这就需要 C# 的编译器能够实现下面两个特性以支持 COM 互操作的这个场景:
- ref cmit - 不需要象 C# 2.0 那样使用 ref 进行 COM 参数传递,这样可以省略必要的参数声明。
- 可选参数 - 作为 COM 调用的“重载决策”。
C# 4.0 中的可选参数
合 Visual Basic、Visual C++ 或者 Delphi 一样,C# 声明可选参数的方法就是在方法参数声明后面加上参数的默认值。
1: [Attribute1, [Attribute2, ...]]
2: public | internal | protected | internal protected | private
3: returnType MethodName( [param list]) { ... }
4:
5: param list ::= { param1, [param2, ...] }
6: param ::= type name [= value]
例如,下面的代码声明了可选参数。
1: void Foo1(int a, int b = 1);
2: void Foo2(int a, string b, int c = 0);
3: string Foo3(string a, string b = "any", Version c = new Version());
和其他语言一样,可选参数必须从右到左出现在参数列表中,也就是说可选参数的右边(下一个参数)一定是可选参数。这些声明是非法的。
1: // Optional parameter does not appear to the right side.
2: void Foo1(int a = 0, int b);
3: // Optional parameter type mismatch.
4: void Foo2(int b = default(double));
在代码中这样调用带有可选参数的函数。
可见,可选参数的赋值需要使用命名参数。命名参数是指在调用带有可选参数函数时,通过参数名称直接对对应的参数赋值的方法。这在可选参数函数调用时可以大大简化语法。
在上面的例子中,我们需要注意,Foo 函数最少需要 2 个非可选参数值。如果将最后一个函数调用 Foo(a: 1, c: 2, b: 1) 改成 Foo(a: 1, c: 2),那么编译器会报告一个缺少参数的错误,如图。
同样的,不能像 Visual Basic 那样省略参数列表,例如,要为可选参数 e 指定值,不能用这样的语句:Foo(1, 2, , , new Version()),而必须通过命名参数来解决此问题(上图中的第 4 个函数调用)。