一、类的继承
Timothy:继承的本质是派生类在基类已有的成员基础上,对基类进行的横向和纵向的扩展。
• 横向扩展:对类成员个数的扩充
• 纵向扩展:对类成员(重写)版本的更新
只能扩展不能缩减,这是静态类型语言(C#、C++、JAVA 等)的特征,继承时类成员只能越来越多。
动态类型语言(Python、JavaScript)可以移除类成员。
继承的特点是:
子类会获得所有父类所具备的成员,子类中还可以在父类基础上添加成员形成横向扩展,也可以对父类成员就行修改(重写)从而形成纵向扩展。
二、子类对父类方法的重写
当子类需要修改来自父类的某个成员时,可以采用重写(override)的方式。
因为类成员个数还是那么多,只是更新被重写成员的版本,所以又称为纵向扩展。
注:重写时,Car 里面只有一个版本的 Run。
重写需要父类成员标记为 virtual,子类成员标记 override。
注:被标记为 override 的成员,隐含也被是 virtual 的,可以继续被重写
class Program
{
static void Main(string[] args)
{
var car = new Car();
car.Run();
// Car is running!
var v = new Vehicle();
v.Run();
// I'm running!
}
}
class Vehicle
{
public virtual void Run()
{
Console.WriteLine("I'm running!");
}
}
class Car : Vehicle
{
public override void Run()
{
Console.WriteLine("Car is running!");
}
}
Hide
如果子类和父类中函数成员签名相同,但又没标记 virtual 和 override,称为 hide 隐藏,会在car类内形成两个run方法,一个是从 Vehicle 继承的 base.Run(),一个是自己声明的 this.Run()。
三、父类对象 通过子类构建形成的多态
当父类类型的变量引用子类类型的实例,成员的具体行为(版本)由构建对象的构建器决定。
class Program
{
static void Main(string[] args)
{
Vehicle v = new RaceCar();//v是一个Vehicle类成员但在构件时使用了子类ReceCar进行构建,因此他的run方法会从Vehicle中的run方法出发,沿着继承链找到到ReceCar类时的最新版本run方法(最后一个重写版本的run),这里就是RaceCar类内部的run方法。故输出结果为Race car is running!
v.Run();
// Race car is running!
Car c = new RaceCar();
c.Run();
// Race car is running!
Console.ReadKey();
}
}
class Vehicle
{
public virtual void Run()
{
Console.WriteLine("I'm running!");
}
}
class Car : Vehicle
{
public override void Run()
{
Console.WriteLine("Car is running!");
}
}
class RaceCar : Car
{
public override void Run()
{
Console.WriteLine("Race car is running!");
}
}