7.3 所有类都派生自object类
除了特殊的类 object ,所有的类都是派生类,即使它们没有基类规格说明
类 object 是唯一的非派生类,因为它是继承层次结构的基础
没有基类规格说明的类隐式地直接派生自类 object
所有的类都是派生类,要么派生自object,要么派生自其他的类。所以,我们通常称一个类为派生类时,意思是它直接派生自某类而不是 object
7.4 屏蔽基类的成员
派生类不能删除它继承的任何成员,但可以用与基类成员名称相同的成员来屏蔽基类成员。
这是继承的主要功能之一。
- 要屏蔽一个继承的数据成员,需要声明一个新的相同类型的成员,并使用相同的名称
- 通过在派生类中声明新的带有相同签名的函数成员,可以隐藏或屏蔽继承的函数成员
请记住,签名由名称和参数列表组成,不包括返回类型 - 要让编译器知道你在故意屏蔽继承的成员,使用 new 修饰符。否则,程序可以成功编译,但编译器会警告你隐藏了一个继承的成员
- 也可以屏蔽静态成员
class SomeClass //基类
{
public string Field1;
...
}
class OtherClass : SomeClass //派生类
{
new public string Field1; //用同样的名称屏蔽基类成员
...
}
7.5 基类访问
如果派生类必须完全地访问被隐藏地继承成员,可以使用基类访问(base access)表达式访问隐藏地继承成员。基类访问表达式由关键字 base 后面跟着一个点和成员地名称组成,如下所示:
Console.WriteLine("{0}", base.Field1); ⇢ 基类访问
下面代码派生类 OtherClass 隐藏了基类地 Field1,但可以使用基类访问表达式访问它
class SomeClass //基类
{
public string Field1 = "Field1 -- In the base class";
}
class OtherClass : SomeClass //派生类
{
new public string Field1 = "Field1 -- In the derived class";
public void PrintField1()
{
Console.WriteLine(Field1); //访问派生类
Console.WriteLine(base.Field1); //访问基类
}
}
class Program
{
static void Main()
{
OtherClass oc = new OtherClass();
oc.PrintField1();
}
}
结果如下:
Field1 -- In the derived class
Field1 -- In the base class
如果你的程序经常使用这个特性,可能需要重新评估类的设计。一般来说,能有更优雅的设计,但在其他没办法的时候也可以使用这个特性。
7.6 使用基类的引用
派生类的实例由基类的实例加上派生类新增的成员组成。派生类的引用指向整个类对象,包括基类部分。
MyDerivedClass derived = new MyDerivedClass(); //创建一个对象
MyBaseClass mybc = (MyBaseClass) derived; //转换引用
- 第一行声明并初始化了变量 MyBaseClass,包含一个 MyDerivedClass类型对象的引用
- 第二行声明了一个基类类型 MyBaseClass 的变量,并把 derived 中的引用转换为该类型,给出对象的基类部分的引用
基类部分的引用被 储存在变量 mybc 中,在赋值运算符的左边
基类部分的引用不能 “ 看到 ”派生类对象的其余部分,因为它通过基类类型的引用 “ 看 ”到这个对象
7.7 构造函数的执行
- 要创建对象的基类部分,需要隐式调用基类的某个构造函数作为创建实例过程的一部分
- 继承层次链中的每个类在执行它自己的构造函数体之前执行它的基类构造函数
构造的顺序如下图,创建一个实例过程中完成的第一件事是初始化对象的所有实例成员。之后,调用基类的构造函数,然后才执行该类自己的构造函数体。
下面代码展示了执行顺序 1,2,3:
class MyDerivedClass : MyBaseClass
{
int MyField1 = 5; //1. 成员初始化
int MyField2; // 成员初始化
public MyDerivedClass() //3. 构造函数体执行
{
...
}
}
class MyBaseClass
{
public MyBaseClass() //2. 基类构造函数调用
{
...
}
}
7.7.1 构造函数初始化语句
默认情况下,在构造对象是,将调用基类的无参数构造函数。但构造函数可以重载,所以基类可能有一个以上的构造函数。如果希望派生类使用一个指定的基类构造函数而不是无参数构造函数,必须在 构造函数初始化语句 中指定它。
有两种形式的构造函数初始化语句:
- 第一种使用关键字base并指明使用哪一个基类构造函数
- 第二种使用关键字this并指明应该使用当前类的哪一个构造函数
基类构造函数初始化语句放在冒号后面,冒号紧跟着类的构造函数声明的参数列表。构造函数初始化语句由关键字base和要调用的基类构造函数的参数列表组成。
下面展示了类 MyDerivedClass 的构造函数
public MyDerivedClass( int x, string s ) : base( s, x )
{}
- 构造函数初始化语句指明要使用有两个参数的基类构造函数,并且第一个参数是一个int, 第二个参数是一个string
- 在基类参数列表中的参数必须在类型和顺序方面与已定的基类构造函数的参数列表相匹配
当声明一个不带构造函数初始化语句的构造函数时,它实际上是带有base()构造函数初始化语句的简单形式,下图中两种形式是等价的
第二种形式的构造函数初始化语句可以让构造过程(编译器)使用当前类中其他的构造函数。下例中MyClass类包含带有一个参数的构造函数,但这个单参数的构造函数使用了同一类中有两个参数的构造函数,为第二个参数提供了一个默认值。
public MyClass( int x ) : this( x, "Using Default String" )
{}
此语法有用的另一种情况是,一个类有好几个构造函数,且都需要在对象构造的过程开始时执行一些公共的代码。对这种情况,可把公共代码提取出来作为一个构造函数,被其他所有的构造函数作为构造函数初始化语句使用,减少了重复的代码。