我们不妨看个例子:
using System;
namespace Test
{
class Grandpapa
{
protected string house="房子";
}
class Father:Grandpapa
{
protected string car="车子";
}
class Son:Father
{
protected string wife="妻子";
public void ShowRiches()
{
Console.WriteLine("我的财产有:爷爷的{0},父亲的{1},我的{2}",house,car,wife);
}
}
class Program
{
public static void Main(string [] args)
{
Son me=new Son();
me.ShowRiches();
}
}
}
运行结果:
我的财产有:爷爷的房子,父亲的车子,我的妻子
上面例子当中我们定义了三个类,分别是Grandpapa(爷爷)、Father(父亲)、Son(儿子)。在每一个类中我们仅仅定义了一个保护型的成员。从这个例子你会清楚地看到继承就好像接力棒,一级一级地往下传递东西。父亲拥有自己和爷爷的财产,而儿子又拥有父亲、爷爷和自己的财产,如此往下传递,子子孙孙生生不息。
基对象引用子类实例——父母可以代表孩子做很多事情
继承不仅可以使创建子类变得简单,而且继承还有一个重要的特性,那就是基类对象可以引用子类实例,简单点说就是子类实例可以赋值给基类对象变量。这是C#同C++、Java都有的特性,这个特性实际上并不难以理解,一旦子类将实例交给基类,可以说是由基类代表子类行使功能。我们现实生活中也有很多这样的例子,比如你的父母可以代表你做很多事情,比如总公司的老板可以代表各个子公司行使权利等。这种特性使得某些功能变得规范,规范的功能便于扩展和灵活使用,这就是这个特性所带来的益处。我们来看一个实际的例子你就明白了:
using System;
namespace Test
{
class TV
{
private string mode;
public void Show() { Console.WriteLine("放电视");}
public static TV GETSONYTv() { return new SONYTv();}
public static TV GETTCLTv() { return new TCLTv();}
}
class SONYTv : TV
{
public void PlayGame() { Console.WriteLine("玩游戏"); }
}
class TCLTv : TV
{
public void PlayMusic() { Console.WriteLine("听音乐"); }
}
class Test
{
[STAThread]
static void Main(string [] args)
{
TV tv=TV.GETSONYTv(); //获得Sony电视
tv.Show(); //播放Sony电视
tv=TV.GETTCLTv(); //得到TCL电视
tv.Show(); //播放TCL电视
((TCLTv)tv).PlayMusic(); //使用TCL电视播放音乐
}
}
}
运行结果:
放电视
放电视
听音乐
上面的例子我们定义了一个电视基类TV,然后定义了其两个子类TCLTv和SONYTv,我们使用基类的对象引用了这两个子类的实例,并分别调用Show方法来播放电视。事实上对于一般用户而言,电视就需要播放功能,我们如此使用两种电视的播放功能你感觉到简单了么(对于普通用户只需要学习TV类的使用即可),这就是基类这个特性所带来的便利。要注意基类对象如果引用了子类实例,那么只能调用基类定义的那部分,如果你还想调用子类的那部分,那么你还需将基类对象强型类型转换为子类对象方可,强行类型转换的方式和值类型相同,使用“(目标类型)”来进行,参照上面代码。我们上面只是展示了一层继承的情况,事实上多层继承情况也与此类似,比如爷爷类对象可以引用孙子类实例,此时调用也只能调用孙子类从爷爷类继承的那部分。
base关键字
通过前面的介绍,我们知道子类只能继承父类非private访问修饰符的数据和方法成员,子类不能继承父类的构造方法和析构方法。根据编译器的内部机制,我们知道在创建子类实例对象时会先自动创建其相关的父类对象,我们知道系统在创建对象时必须调用其类的构造方法,那么在创建子类对象时父类的构造方法是怎样被调用的呢?很简单,是自动被调用的!
示例:
using System;
namespace Test
{
class MyBase
{
public MyBase()
{
Console.WriteLine("基类对象被创建");
}
~MyBase()
{
Console.WriteLine("基类对象被销毁");
}
}
class SubClass : MyBase
{
public SubClass()
{
Console.WriteLine("子类对象被创建");
}
~SubClass()
{
Console.WriteLine("子类对象被销毁");
}
}
class Program
{
static void Main(string [] args)
{
SubClass sc=new SubClass();
}
}
}
运行结果:
基类对象被创建
子类对象被创建
子类对象被销毁
基类对象被销毁
很显然,不仅是基类构造方法,基类析构方法也是被自动调用的,调用的顺序如上所示:“
创建基类对象→创建子类对象→销毁子类对象→销毁父类对象”。如果基类的构造方法没有重载或没有定义,那么系统在创建子类对象时将自动调用基类的默认构造方法来创建基类对象。还有一个问题,我们说类的构造方法可以重载,此时,我们就需要在子类里使用base关键字来指定父类调用哪个构造方法。当然,base关键字的作用不仅仅如此,它还可以访问从基类继承过来的成员,但要注意不能在子类的静态方法中使用base关键字访问基类成员。如果基类的构造方法有多个,那么子类可以使用base关键字来指定应该调用哪一个,使用base指定调用父类构造方法的语法如下:
子类构造方法:base(参数列表)