哪个更好?乍一看,可选参数似乎更好(代码更少,XML文档也更少等),但为什么大多数MSDN库类使用重载而不是可选参数?
当你选择使用可选参数(或重载)时,有什么特别的事情需要注意吗?
哪个更好?乍一看,可选参数似乎更好(代码更少,XML文档也更少等),但为什么大多数MSDN库类使用重载而不是可选参数?
当你选择使用可选参数(或重载)时,有什么特别的事情需要注意吗?
在C# 4.0中,“Optional parameters”与“Named Parameters”结合使用的一个好用例是,它为我们提供了一种优雅的方法重载替代方案,而不是基于参数数量进行方法重载。
例如说你想要一个名为foo
的方法可以这样被调用和使用:foo()
、foo(1)
、foo(1,2)
、foo(1,2, "hello")
。使用方法重载,你需要实现以下代码:
///Base foo method
public void DoFoo(int a, long b, string c)
{
//Do something
}
/// Foo with 2 params only
public void DoFoo(int a, long b)
{
/// ....
DoFoo(a, b, "Hello");
}
public void DoFoo(int a)
{
///....
DoFoo(a, 23, "Hello");
}
.....
在C# 4.0中使用可选参数,您可以按照以下方式实现用例:
public void DoFoo(int a = 10, long b = 23, string c = "Hello")
那么您可以像这样使用该方法-请注意使用了命名参数-
DoFoo(c:"Hello There, John Doe")
这个调用需要将参数a
的值设置为10,参数b
的值设置为23。
此调用的另一种变体 - 注意您不需要按照方法签名中的顺序设置参数值,命名参数使值明确。
DoFoo(c:"hello again", a:100)
使用命名参数的另一个好处是极大地提高了可选参数方法的可读性,从而增强了代码的可维护性。
请注意,一种方法基本上使得定义3个或更多方法在方法重载中变得多余。我发现这是使用可选参数与命名参数结合使用的一个好用例。
可选参数在将其作为API公开时会产生问题。重命名参数可能会导致问题。更改默认值也会导致问题(例如,有关一些信息,请参见此处:C# 4.0可选参数的注意事项)。
另外,可选参数只能用于编译时常量。比较以下两者:
public static void Foo(IEnumerable<string> items = new List<string>()) {}
// Default parameter value for 'items' must be a compile-time constant
变成这样
public static void Foo() { Foo(new List<string>());}
public static void Foo(IEnumerable<string> items) {}
//all good
当一个带有默认参数的构造函数与反射不兼容时,这里提供了一些附加阅读材料:does not play nicely with Reflection。
我认为它们有不同的用途。可选参数用于当您可以为参数使用默认值时,底层代码将相同:
public CreditScore CheckCredit(
bool useHistoricalData = false,
bool useStrongHeuristics = true) {
// ...
}
方法重载是用于具有互斥(子集)参数的情况。这通常意味着您需要预处理一些参数,或者对于不同的方法“版本”,您需要完全不同的代码(请注意,即使在这种情况下,某些参数也可以共享,这就是我上面提到“子集”的原因):
public void SendSurvey(IList<Customer> customers, int surveyKey) {
// will loop and call the other one
}
public void SendSurvey(Customer customer, int surveyKey) {
...
}
(我之前写过一篇文章,关于这个问题,在这里)
这个几乎是不言自明的,但是:
并非所有语言都支持可选参数。如果您希望您的库对这些语言友好,您需要使用重载。
诚然,这对于大多数公司来说甚至都不是问题。但你可以打赌这就是微软在基类库中没有使用可选参数的原因。
两者并没有明确的“更好”之分,它们在编写良好代码方面都有自己的用处。如果参数可以具有默认值,则应使用可选参数。当签名的差异超出未定义可能具有默认值的参数时(例如行为因传递哪些参数以及将哪些参数留给默认值而不同),应该使用方法重载。
// this is a good candidate for optional parameters
public void DoSomething(int requiredThing, int nextThing = 12, int lastThing = 0)
// this is not, because it should be one or the other, but not both
public void DoSomething(Stream streamData = null, string stringData = null)
// these are good candidates for overloading
public void DoSomething(Stream data)
public void DoSomething(string data)
// these are no longer good candidates for overloading
public void DoSomething(int firstThing)
{
DoSomething(firstThing, 12);
}
public void DoSomething(int firstThing, int nextThing)
{
DoSomething(firstThing, nextThing, 0);
}
public void DoSomething(int firstThing, int nextThing, int lastThing)
{
...
}
可选参数必须放在最后。因此,除非该参数也是可选的,否则您不能向该方法添加额外的参数。例如:
void MyMethod(int value, int otherValue = 0);
void MyMethod(int value, int otherValue = 0, int newParam = 0);
如果无法选择,则必须使用重载并删除“otherValue”的可选值。如下所示:
void MyMethod(int value, int otherValue = 0);
void MyMethod(int value, int otherValue, int newParam);
void MyMethod(int value, int otherValue = 0, int newValue = 0);
MyMethod(10, newValue: 10); // Here I omitted the otherValue parameter that defaults to 0
因此,可选参数为调用者提供了更多的可能性。
最后一件事。如果您使用一个实现的方法重载,就像这样:
void MyMethod(int value, int otherValue)
{
// Do the work
}
void MyMethod(int value)
{
MyMethod(value, 0); // Do the defaulting by method overloading
}
然后当像这样调用'MyMethod':
MyMethod(100);
将会导致2个方法调用。但是,如果您使用可选参数,则只有一个'MyMethod'的实现,因此只有一个方法调用。
这并不是对原问题的回答,而是对 @NileshGule 的 回答 的评论:
a) 我没有足够的声望来发表评论
b) 在评论中阅读多行代码很困难
Nilesh Gule 写道:
使用可选参数的好处之一是,如果其中一个输入参数是字符串,您无需在方法中进行条件检查,例如字符串是否为 null 或为空。由于可选参数将分配默认值,因此防御性编程将大大减少。
实际上,这是不正确的,您仍然需要检查空值:
void DoSomething(string value = "") // Unfortunately string.Empty is not a compile-time constant and cannot be used as default value
{
if(value == null)
throw new ArgumentNullException();
}
DoSomething(); // OK, will use default value of ""
DoSomething(null); // Will throw