【发布时间】:2010-10-04 09:03:15
【问题描述】:
在继承中,为什么基类构造函数首先得到调用,为什么派生的没有??
【问题讨论】:
在继承中,为什么基类构造函数首先得到调用,为什么派生的没有??
【问题讨论】:
确保基类的公共或受保护成员在派生类中使用之前已正确初始化。
准确地说,派生类构造函数首先运行,隐式调用基类构造函数,编译器作为派生类构造函数主体中的第一条语句插入(假设默认无参数构造函数)。
【讨论】:
派生类是通过扩展基类创建的。应确保基类成员已正确初始化,然后才能在派生类中扩展。此外,在派生类中初始化的成员不应被基类覆盖。
【讨论】:
想想如果反过来会发生什么。让我们想象一个具有_id 值的用户类。 0 的_id 是一个特殊值,代表“来宾”帐户(忽略围绕“特殊值”的问题,首先它们并不总是一个坏主意,其次这只是一个示例)。 _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 类的规则之一是访客永远不能成为管理员。这个不变量应该在客人状态可以改变的所有点上强制执行,在这种情况下只在构造函数中:
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 设置为一个先决条件。
【讨论】:
什么先出生,父母还是孩子?
【讨论】: