可选参数或重载的构造函数


28

我正在实现DelegateCommand,当我要实现构造函数时,我想到了以下两种设计选择:

1:具有多个重载的构造函数

public DelegateCommand(Action<T> execute) : this(execute, null) { }

public DelegateCommand(Action<T> execute, Func<T, bool> canExecute)
{
    this.execute = execute;
    this.canExecute = canExecute;
}

2:只有一个带有可选参数的构造函数

public DelegateCommand(Action<T> execute, Func<T, bool> canExecute = null)
{
    this.execute = execute;
    this.canExecute = canExecute;
}

我不知道使用哪个,因为我不知道这两种建议的方式都有哪些优点/缺点。两者都可以这样称呼:

var command = new DelegateCommand(this.myExecute);
var command2 = new DelegateCommand(this.myExecute, this.myCanExecute);

有人可以指出正确的方向并提供反馈吗?


4
吻和亚尼 如有疑问,请同时实施。片刻之后,搜索对两个构造函数的引用。如果其中一个从未在外部使用过,这不会令我感到惊讶。或略有消耗。在执行代码的静态分析时,这很容易找到。
Laiv

1
静态构造函数(如Bitmap.FromFile)也是一个选项
BlueRaja-Danny Pflughoeft 18-3-27

3
遵守代码分析规则CA1026:不应使用默认参数
丹·里昂斯

2
@Laiv我想念什么吗?它们以相同的方式使用,因此具有相同的签名。您无法搜索参考并告诉呼叫者在想什么。听起来更像是您在指的是OP是否应该再包含第二个参数,但这不是OP所要问的(他们可能很清楚地知道有时需要两者,因此他们会问)。
凯特

2
@Voo Openedge | Progress 10.2B的编译器将忽略它们。几乎杀死了我们不变的方法变更策略;相反,我们需要使用重载来达到相同的效果。
Bret

Answers:


24

与默认值相比,我更喜欢多个构造函数,而且我个人不喜欢您的两个构造函数示例,它的实现方式应不同。

使用多个构造函数的原因是,主要构造函数仅可以检查所有参数是否都不为null以及它们是否有效,而其他构造函数可以为主要构造函数提供默认值。

但是,在您的示例中,它们之间没有区别,因为即使辅助构造函数也将a null作为默认值传递,并且主要构造函数也必须知道默认值。我认为不应该。

这意味着如果以这种方式实现,它将更加干净和隔离:

public DelegateCommand(Action<T> execute) : this(execute, _ => true) { }

public DelegateCommand(Action<T> execute, Func<T, bool> canExecute)
{
    this.execute = execute ?? throw new ArgumentNullException(..);
    this.canExecute = canExecute ?? throw new ArgumentNullException(..);
}

请注意,_ => true传递给主构造函数的对象现在也正在检查所有参数,null并且不关心任何默认值。


但是,最重要的一点是可扩展性。如果将来有可能扩展代码,则多个构造函数会更安全。如果添加更多必需的参数,并且必须在最后添加可选参数,那么您将破坏当前的所有实现。您可以创建旧的构造函数,[Obsolete]并通知用户它将被删除,从而使他们有时间迁移到新的实现,而无需立即破坏他们的代码。


另一方面,将过多的参数设置为可选参数也会造成混淆,因为如果在一种情况下需要使用某些参数,而在另一种情况下则需要使用可选参数,则您需要研究文档,而不是仅仅通过查看参数来选择合适的构造函数。


12
另一方面,将太多的参数设置为可选参数会令人困惑...老实说,拥有太多的参数(无论它们是否可选)都令人困惑。
安迪

1
@DavidPacker我同意你的观点,但是我认为另一个参数太多了;-)
t3chb0t

2
具有省略参数的构造函数与具有参数默认值的构造函数之间还有另一个区别。在第一种情况下,调用者可以明确避免传递参数,而在后一种情况下,调用者不能-因为不传递参数与传递默认值相同。如果构造函数需要进行区分,那么只有两个构造函数是唯一的选择。
加里·麦吉尔

1
例如,假设我有一个格式化字符串的类,我可以选择使用一个CultureInfo对象来构造它。我宁愿允许的API CultureInfo,以通过附加参数提供的参数,但如果非要说这供给,那么它应该不会null。这样,意外null值就不会被误解为“无”。
加里·麦吉尔

我不确定可扩展性是否真的是反对可选参数的原因;如果您根本不需要参数,并且更改了参数个数Obsolete,则无论是否还有可选参数,都必须创建一个新的构造函数和一个旧的构造函数,以免造成麻烦。使用可选参数时,只需Obsolete删除一个即可。
Herohtar

17

考虑到构造函数中唯一要做的事情就是简单的赋值,因此单一构造函数解决方案可能是您更好的选择。另一个构造函数没有提供额外的功能,从带有两个参数的构造函数的设计中很明显,不需要提供第二个参数。

当您从不同类型构造对象时,多个构造函数确实有意义。在那种情况下,构造函数是外部工厂的替代,因为它们处理输入参数并将其格式化为正在构造的类的正确内部属性表示。但是,这不会在您的情况下发生,因此,为什么一个单独的构造函数应该绰绰有余。


3

我认为每种方法都有两种不同的情况。

首先,当我们有简单的构造函数时(根据我的经验,通常是这种情况),我将考虑使用可选参数。

  1. 它们将必须编写的代码(因此也必须读取的代码)最小化。
  2. 您可以确保文档在一个地方并且不会重复(这很糟糕,因为它打开了一个可能会过时的额外区域)。
  3. 如果您有许多可选参数,则可以避免使用一组非常混乱的构造函数组合。哎呀,即使只有两个可选参数(彼此无关),如果要使用单独的,重载的构造函数,则也必须有4个构造函数(版本不带任何版本,版本不带版本,版本不带版本)。这显然不能很好地扩展。

Buuuut,在某些情况下,构造函数中的可选参数只会使事情变得更加混乱。最明显的例子是这些可选参数是互斥的(即不能一起使用)。重载仅允许参数实际有效组合的构造函数将确保在编译时强制执行此约束。也就是说,您还应该避免遇到这种情况(例如,从基类继承多个类,每个类都具有排他性行为)。


+1表示具有多个可选参数的置换爆炸。
Jpsy
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.