嵌套构造函数(或工厂方法)好还是每个构造函数都应该完成所有初始化工作?

32

在设计中,为重载的New或工厂方法嵌套构造函数调用是否是一个好主意?这主要适用于简单的构造函数,每个重载都基于前一个构造函数。

MyClass( arg1 ) { 
    _arg1 = arg1; 
    _otherField = true; 
    _color="Blue" 
}
MyClass( arg1, arg2) : this(arg1) { 
    _arg2 = arg2  
}
MyClass( arg1, arg2, arg3) : this(arg1, ar2) { 
    _arg3 = arg3; 
}

或者使用工厂方法:

static NewInstance(arg1 ) { 
   _arg1 = arg1;       
}
static NewInstance(arg1, arg2) {
   f = NewInstance(arg1);
   f._arg2 = arg2;
}
//... and so on

我能看到双方都存在一些缺点。

  • 嵌套会隐藏构造函数正在执行的操作
  • 不嵌套会复制所有功能

那么,这样做是一个好主意吗,还是它让我面临了我没有意识到的问题?由于它分散了初始化的责任,某种程度上我感到不安。

编辑: @Jon Skeet: 我现在明白为什么这件事情困扰着我。我刚开始做反了!我把整个东西写完了,甚至没注意到,只是感觉有些怪怪的。我写的大多数其他案例都按照您建议的方式进行处理,但这显然不是我所做的唯一一种。我注意到我处理更复杂的案例时都做得很好,但在简单的案例中却似乎变得有些松懈。 我喜欢微小的修改。我也喜欢首字母缩写词!

2个回答

63

我认为将构造函数链接在一起是合理的,但我却采用了另一种方式 - 参数较少的版本调用参数较多的版本。这样可以非常清楚地表明正在发生什么,并且所有真正的“逻辑”(除了默认值)都在一个地方。例如:

public Foo(int x, int y)
{
    this.x = x;
    this.y = y;
    precomputedValue = x * y;
}

private static int DefaultY
{
    get { return DateTime.Now.Minute; }
}

public Foo(int x) : this(x, DefaultY)
{
}

public Foo() : this(1, DefaultY)
{
}
请注意,如果您有大量的构造函数重载,您可能希望改为使用静态工厂方法 - 这通常会使代码更清晰,并允许多个方法采用相同的参数集,例如。
public static XmlDocument FromText(string xml)

public static XmlDocument FromFile(string filename)

不幸的是,这种技术对于运行时默认值无效。在示例中,您展示了可选参数的常量。如果这些值不是常量,那么您将无法编译代码-您必须切换到在构造函数体中提供值的方法。 - JeremyDWill
不,它们可以是通过执行静态方法/属性获取的运行时值。编辑答案以给出一个示例。 - Jon Skeet
你仍然将值移动到方法体中,只是没有移动到构造函数体中。这并没有真正改变情况。 - JeremyDWill
1
区别在于这些评估是有名称的 - 因此,如果您适当地命名事物,仍然很明显正在发生什么。 您不需要详细查看每个构造函数。 说实话,在我的经验中,这很少需要 - 常量或单个表达式更常见。 - Jon Skeet
1
我认为各有所好。根据我的经验,默认值来自文件、数据库或计算结果。因此,创建额外的静态方法仅用于服务于这种构造模式比在构造函数中执行赋值操作更差。感谢交流! - JeremyDWill
显示剩余4条评论

-7

2016年编辑:C#仍然领先于时代,它在C#7中彻底削减或取消了记录支持和默认构造函数支持,也许在C#8中才会最终实现。

2015年编辑:我领先于时代。C#6和C#7正在消除对构造函数的需求。

如果您正在开发.Net 3.5,我建议永远不要使用构造函数。唯一的例外是,如果您正在使用注入构造函数进行依赖项注入。在.Net 3.5中,他们创建了对象初始化程序,允许您执行以下操作:

var myclass = New MyClass { arg1 = "lala", arg2 ="foo" }

这将使用为arg1和arg2分配的值来初始化类,同时将arg3保留为空。

default(typeof(arg3)).

8
任何使用只读变量的机会都荡然无存了。 - Jon Skeet
2
我不认为字符串和整数是依赖项 - 但它们往往是只读成员。例如,您的“MyClass”示例肯定可以将arg1和arg2设置为只读。幸运的是,使用可选和命名参数,C# 4会使这个过程稍微容易一些。 - Jon Skeet
2
所以...你的意思是基本上使用带有命名值的构造函数?哦,我真的很喜欢暴露所有私有内部,只是为了获得一个更丑陋的初始化语法。太好了,如果我需要命名参数,我会使用它们。 - Andrew
1
不是所有东西都必须在构造函数/对象初始化器中设置!构造函数的存在是为了提供方便快捷的对象初始化方式,而不是一次性设置每个属性的方法。你的论点似乎认为你反对手动设置属性。除非使用私有/内部字段,否则构造函数也是配置内部状态的唯一方式(特别是如果它依赖于使用的构造函数签名),就像安德鲁所暗示的那样。 - Chris Laplante
4
不正确。后备字段一直与属性一起使用。 - Chris Laplante
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接