方法重载 vs C # 4.0中的可选参数

哪个更好?乍一看,可选参数似乎更好(更少的代码、更少的 XML 文档等) ,但为什么大多数 MSDN 库类使用重载而不是可选参数?

当您选择使用可选参数(或重载)时,是否有什么特别的事情需要注意?

81430 次浏览

可选参数必须是最后一个。因此,不能向该方法添加额外的参数,除非它也是可选的。例如:

void MyMethod(int value, int otherValue = 0);

如果你想在不重载的情况下给这个方法添加一个新的参数,它必须是可选的

void MyMethod(int value, int otherValue = 0, int newParam = 0);

如果它不能是可选的,那么你必须使用重载,并删除“ other Value”的可选值。像这样:

void MyMethod(int value, int otherValue = 0);
void MyMethod(int value, int otherValue, int newParam);

我假设您希望保持参数的顺序不变。

因此,使用可选参数可以减少类中需要的方法的数量,但是它们的数量有限,因为它们必须是最后一个。

更新 When calling methods with optional parameters, you can used named parameters like this:

void MyMethod(int value, int otherValue = 0, int newValue = 0);


MyMethod(10, newValue: 10); // Here I omitted the otherValue parameter that defaults to 0

因此,可选参数为调用者提供了更多的可能性。

One last thing. If you use method overloading with one implementation, like this:

void MyMethod(int value, int otherValue)
{
// Do the work
}


void MyMethod(int value)
{
MyMethod(value, 0); // Do the defaulting by method overloading
}

Then when calling 'MyMethod' like this:

MyMethod(100);

将导致2个方法调用。但是如果您使用可选参数,那么‘ MyMethod’只有一个实现,因此只有一个方法调用。

C # 4.0中“可选参数”和“命名参数”的一个很好的用例是,它为我们提供了一个优雅的方法重载替代方法,即根据参数数量重载方法。

For example say you want a method foo to be be called/used like so, foo(), foo(1), foo(1,2), foo(1,2, "hello"). With method overloading you would implement the solution like this,

///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")

This call takes parameter a value as 10 and parameter b as 23. 这个调用的另一个变体——注意,不需要按照方法签名中出现的顺序设置参数值,命名参数使得值显式化。

DoFoo(c:"hello again", a:100)

使用命名参数的另一个好处是,它极大地提高了可读性,从而增强了可选参数方法的代码维护。

Note how one method pretty much makes redundant having to define 3 or more methods in method overloading. This I have found is a good use case for using optional parameter in conjunction with named parameters.

使用可选参数的一个好处是,您不必在方法中执行条件检查,例如,如果输入参数之一是字符串,那么字符串是否为空或为空。由于为可选参数分配了一个默认值,因此防御性编码将在很大程度上减少。

命名参数提供了以任意顺序传递参数值的灵活性。

两者都不是绝对的“更好”。他们在编写优秀代码方面都有自己的位置。如果参数可以有默认值,则应使用可选参数。当签名的差异超出了没有定义可能具有默认值的参数的范围(例如,根据传递的参数和默认值的不同,行为也会有所不同)时,应该使用方法重载。

// 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)
{
...
}

这几乎是不言而喻的,但是:

并非所有语言都支持可选参数。如果希望库对这些语言友好,就必须使用重载。

Granted, this isn't even an issue for most shops. But you can bet it's why Microsoft doesn't use optional parameters in the Base Class Library.

当您将可选参数作为 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

to this

public static void Foo() { Foo(new List<string>());}
public static void Foo(IEnumerable<string> items) {}
//all good

更新

当构造函数使用默认参数 不能很好的反射时,下面是一些额外的阅读材料。

回答你的第一个问题,

why do most MSDN library classes use 过载而不是可选的 参数?

It is for backward compatibility.

当您在 VS2010中打开 C # 2、3.0或3.5项目时,它会自动升级。

想象一下,如果必须对项目中使用的每个重载进行转换以匹配相应的可选参数声明,那么将会造成多大的不便。

另外,俗话说得好,“为什么要修复没有坏掉的东西呢?”.没有必要替换已经在新实现中工作的重载。

我相信它们有不同的用途。可选参数用于当您可以为参数使用默认值时,并且基础代码将是相同的:

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) {
...
}

(前段时间我写过这个)

第三个选项怎么样: 传递一个类的实例,其属性对应于各种“可选参数”。

这提供了与命名参数和可选参数相同的好处,但我觉得这通常要清楚得多。它使您有机会在必要时对参数进行逻辑分组(例如使用组合) ,并封装一些基本的验证。

Also, if you expect clients that consume your methods to do any kind of metaprogramming (such as building linq expressions involving your methods), I think that keeping the method signature simple has its advantages.

使用可选参数的好地方是 周转基金,因为它不支持方法重载。

这实际上不是对原始问题的回答,而是对@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

如果提供空字符串引用,它将不会被默认值替换。因此,您仍然需要检查空值的输入参数。