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

Lou*_*hys 114 .net c# overloading optional-parameters c#-4.0

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

当您选择使用可选参数(或重载)时,是否有任何特殊事项需要注意?

Bik*_*Lem 77

"可选参数"与C#4.0中"命名参数"结合使用的一个很好的用例是,它为我们提供了一种优雅的方法重载替代方法,您可以根据参数的数量重载方法.

例如说,你想一个方法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");
}

.....
Run Code Online (Sandbox Code Playgroud)

使用C#4.0中的可选参数,您将实现如下所示的用例,

public void DoFoo(int a = 10, long b = 23, string c = "Hello")
Run Code Online (Sandbox Code Playgroud)

然后你可以使用这样的方法 - 注意使用命名参数 -

DoFoo(c:"Hello There, John Doe")
Run Code Online (Sandbox Code Playgroud)

此调用将参数a值设置为10,将参数设置b为23.此调用的另一个变体 - 请注意,您不需要按照它们在方法签名中出现的顺序设置参数值,命名参数使值显式.

DoFoo(c:"hello again", a:100) 
Run Code Online (Sandbox Code Playgroud)

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

注意一种方法如何使得必须在方法重载中定义3个或更多方法变得多余.我发现这是一个很好的用例,可以将可选参数与命名参数结合使用.

  • 所有将要使用此方法的人 - 仅供参考,如果您在调用者中使用命名参数,则无法更改未来参数的名称,除非在每个调用者中更改它(如果所有引用都在同一解决方案中,则这是简单的重构,但不是如果您将其用作其他项目中的库)。话虽如此,无论如何,更改参数名称始终被视为重大更改(即使不使用命名参数!) (2认同)

flq*_*flq 57

当您将它们作为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
Run Code Online (Sandbox Code Playgroud)

对此

public static void Foo() { Foo(new List<string>());}
public static void Foo(IEnumerable<string> items) {}
//all good
Run Code Online (Sandbox Code Playgroud)

更新

当具有默认参数的构造函数与Reflection不能很好地匹配时,这里有一些额外的阅读材料.

  • CALLING站点必须注入默认值这一事实肯定是公共API的一个问题.如果只有我们可以得到一个可选的参数变量,它实际上是用于创建重载的语法糖(同时仍然保留现有的一个用于COM Interop之类的东西). (5认同)
  • 关于重命名参数,无论使用可选参数还是重载方法,这都是一个问题.使用者可以指定命名参数,重命名变量将破坏编译. (2认同)

Jor*_*dão 37

我相信它们有不同的用途.可选参数适用于何时可以使用参数的默认值,底层代码将相同:

public CreditScore CheckCredit( 
  bool useHistoricalData = false,  
  bool useStrongHeuristics = true) { 
  // ... 
}
Run Code Online (Sandbox Code Playgroud)

方法重载适用于具有互斥(子集)参数的情况.这通常意味着你需要预处理一些参数,或者你的方法的不同"版本"完全不同的代码(请注意,即使在这种情况下,也可以共享一些参数,这就是为什么我在上面提到"子集") :

public void SendSurvey(IList<Customer> customers, int surveyKey) {  
  // will loop and call the other one 
} 
public void SendSurvey(Customer customer, int surveyKey) {  
  ...  
}
Run Code Online (Sandbox Code Playgroud)

(我前段时间写了一篇关于这个在这里)

  • 对我来说,这是最好的答案.你是唯一一个区分方法体所做的事情的人.如果方法体是相同的,那么使用可选的参数,这是完全合理的,但是如果方法体将根据参数变化,则使用重载. (12认同)

Joe*_*ite 27

这一点几乎不言而喻,但是:

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

当然,这对大多数商店来说都不是问题.但你可以打赌这就是微软不在基类库中使用可选参数的原因.

  • C#2.0忽略"可选参数"标志,并根据需要处理所有参数.因此,每个呼叫站点都必须为每个参数提供一个值. (10认同)
  • 如果您使用带有不支持它的语言的可选参数的库(例如C#2.0)会发生什么? (2认同)

Mar*_*sen 9

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

void MyMethod(int value, int otherValue = 0);
Run Code Online (Sandbox Code Playgroud)

如果要为此方法添加新参数而不进行重载,则必须是可选的.像这样

void MyMethod(int value, int otherValue = 0, int newParam = 0);
Run Code Online (Sandbox Code Playgroud)

如果它不是可选的,那么您必须使用重载并删除'otherValue'的可选值.像这样:

void MyMethod(int value, int otherValue = 0);
void MyMethod(int value, int otherValue, int newParam);
Run Code Online (Sandbox Code Playgroud)

我假设您希望保持参数的顺序相同.

因此,使用可选参数可以减少您在课堂上需要的方法数量,但是它们必须是最后的.

更新 使用可选参数调用方法时,可以使用如下命名参数:

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

MyMethod(10, newValue: 10); // Here I omitted the otherValue parameter that defaults to 0
Run Code Online (Sandbox Code Playgroud)

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

最后一件事.如果您使用方法重载与一个实现,如下所示:

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

void MyMethod(int value)
{
   MyMethod(value, 0); // Do the defaulting by method overloading
}
Run Code Online (Sandbox Code Playgroud)

然后当这样调用'MyMethod'时:

MyMethod(100); 
Run Code Online (Sandbox Code Playgroud)

将导致2个方法调用.但是如果你使用可选参数,那么只有一个'MyMethod'的实现,因此只有一个方法调用.


Mic*_*ows 9

两者都没有明确的"更好".他们都有写好代码的地方.如果参数可以具有默认值,则应使用可选参数.当签名的差异超出未定义可能具有默认值的参数时(例如,行为根据传递的参数以及哪些参数保留为默认值而不同),应使用方法重载.

// 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)
{
    ...
}
Run Code Online (Sandbox Code Playgroud)