在继承中为什么基类构造函数首先得到调用,为什么不是派生的?
答案 0 :(得分:7)
确保基类的公共成员或受保护成员在派生类中使用之前已正确初始化。
确切地说,派生类构造函数首先运行,对编译器作为派生类构造函数体中的第一个语句插入的基类构造函数进行隐式调用(假设默认为no-arg构造函数)。 p>
答案 1 :(得分:3)
派生类是通过扩展基类创建的。应该确保基类成员在派生类中扩展之前已正确初始化。此外,在派生类中初始化的成员不应被基类覆盖。
答案 2 :(得分:3)
考虑如果是相反的情况会发生什么。让我们设想一个_id
值的用户类。 _id
的0是一个特殊值,表示“访客”帐户(忽略“特殊值”周围的问题,首先它们并不总是一个坏主意,其次这只是一个例子)。 _id
在构造之后也不能改变(这是有道理的,如果它可以被改变,它不再是一个很大的标识符)。
public class User
{
private readonly int _id;
public User(int id)
{
_id = id;
}
public int ID
{
get { return _id; }
}
public bool IsGuest
{
get { return _id == 0; }
}
}
现在考虑一个从此继承的Admin类。 Admin类的一个规则是guest虚拟机永远不能成为管理员。应该在guest状态可以更改的所有点强制执行此不变量,在这种情况下,仅在构造函数中:
public class Admin : User
{
public Admin(int id)
:base(id)
{
if(IsGuest)
throw new SecurityException("Guest users cannot be admins.");
}
}
如果在Admin
之前构造了User
,那么它总会抛出此异常,因为测试总是将0与0进行比较。如果我们对客人有不同的特殊值,那么它会更糟糕,并且永远不会抛出异常,并且允许出现安全问题。
请记住,撰写Admin
课程的人不需要了解User
如何工作,除了有关其公共和受保护界面的文档之外。他们可以通过添加自己的id
是否为零的测试来解决上述问题,但除了这是一个不必要的代码重复之外,没有理由他们应该知道IsGuest
检查是如何工作的,它可能比上面复杂得多,也许可能是专有的,混淆的和无证的。
更一般地说,如果没有“构建用户”的概念作为首先发生的事情,“构建管理员”的整个概念是没有意义的,我们不能在不将X作为X的情况下制作更专业的X类型先决条件。
答案 3 :(得分:2)
首先出生的是父母还是孩子?