繁体   English   中英

构造函数和 Inheritance

[英]Constructors and Inheritance

让我们以 C# 为例

public class Foo
{
    public Foo() { }
    public Foo(int j) { }
}

public class Bar : Foo
{

}

现在, Foo 的所有公共成员都可以在 Bar 中访问,除了构造函数。 我不能做类似的事情

Bar bb = new Bar(1);

为什么构造函数不可继承?

更新

我确实知道我们可以链接构造函数,但我想知道为什么上述构造无效。 我相信应该有一个正当的理由。

构造函数是不可继承的,因为它可能会导致奇怪和意外的行为。 更具体地说,如果您向基础 class 添加了一个新构造函数,则所有派生类都会获得该构造函数的实例。 在某些情况下,这是一件坏事,因为您的基础 class 可能指定了对派生类没有意义的参数。

一个常见的例子是,在许多语言中,所有对象(通常称为“对象”)的基础 class 都有一个没有参数的构造函数。 如果构造函数是继承的,这将意味着所有对象都有一个无参数的构造函数,并且没有办法说“我希望创建此 class 实例的人提供参数 X、Y 和 Z,否则他们的代码不应该编译。 " 对于许多类,为它们的正确 function 定义某些参数很重要,并且使构造函数不可继承是 class 作者可以保证始终定义某些参数的方式的一部分。

编辑以回应评论:Ramesh 指出,如果构造函数是按照他希望的那样继承的,他总是可以在每个派生的 class 中使用私有声明的构造函数来覆盖基本 class 构造函数。 这当然是对的,但这种策略存在后勤问题。 它要求派生类的编写者必须密切关注基类并添加私有构造函数,如果他们想要阻止基 class 构造函数的 inheritance 的话。 这不仅对编写派生类的人来说是很多工作,而且这种跨类的隐式依赖正是那种会导致奇怪行为的事情。

Ramesh - 这并不是说您描述的内容不可能添加到一种语言中。 一般来说,它没有完成,因为这种行为可能会使人们感到困惑并导致大量额外的调试和代码编写。

Quintin Robinson 在绝对值得一读的评论中对这个问题提供了一些非常有价值的回答。

它们是(通过链接),您必须在派生的 object 中链接构造函数。即:

public class Foo
{
    public Foo() { }
    public Foo(int j) { }
}

public class Bar : Foo
{
    public Bar() : base() { }
    public Bar(int j) : base(j) { }
}

然后,派生对象中的构造函数将链接对基础对象中的构造函数的调用。

如果您想进一步阅读,本文提供了更多示例。

您可能会在 class 中引入构造函数的一个原因是,如果没有特定的“依赖关系”,那么拥有该 class 的实例是没有意义的。 例如,它可能是一个必须连接到数据库的数据访问 class:

public class FooRepository
{
    public FooRepository(IDbConnection connection) { ... }
}

如果基类中的所有公共构造函数都可用,则存储库 class 的用户将能够使用 System.Object 的默认构造函数来创建 class 的无效实例:

var badRepository = new FooRepository();

默认情况下隐藏继承的构造函数意味着您可以强制执行依赖项,而不必担心用户创建“无效”实例。

Foo构造函数只能知道如何初始化一个 Foo object,所以它还应该知道如何初始化任何潜在的子类是没有意义的

public class Bar : Foo
{
public Bar(int i) : base(i) { }
}

构造函数讲述的故事是:“嘿,基地 class,请做任何你需要做的工作才能进入一个好的 state,这样我就可以提前 go 并正确设置自己。”

假设构造函数是可继承的。 在许多情况下,如果继承的构造函数对子类没有意义,你将如何禁用它们?

语言设计者没有使用阻止 inheritance 的机制使语言复杂化,而是选择简单地使构造函数不可继承。

由于设计原因,构造函数不可继承。 (请注意,在我所知道的每种面向对象语言中,情况都是一样的。)简单的答案是,在许多情况下,您真的不希望与基本 class 相同的构造函数可用。 有关更完整的解释,请参阅此 SO 线程

一些讨论

基本思想是为创建者提供尽可能多的控制。 你可以拥有私人基地。 那么你是如何创建 object 的呢?

我认为您可以执行以下操作:

public class Bar : Foo
{
  public Bar (int i)
    : base (i)
  {
  }
}

我可能有点不对劲——但这是一般的想法。

简单的答案是该语言不是那样工作的。

不过,您要问的真正问题是为什么它不能那样工作:-) 嗯,这是一个任意选择,它从 C++ 和 Java (以及很可能影响 C# 的许多其他语言)开始。

可能的原因是编译器只会生成一个不接受 arguments 并简单地调用父级的构造函数,如果你想要的不仅仅是编译器让你自己做的事情。 这是最好的选择,因为您所做的不仅仅是调用父构造函数。

实际上,这是因为父构造函数不会完全初始化子 object。 在这方面,构造函数是一种个人的东西。 这就是为什么大多数语言不继承构造函数的原因。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM