父类与子类
父类与子类的关系
- 从编程思想的角度讲,A继承B,意味着A是B。比如汽车继承于交通工具,意味着汽车是交通工具。
// 交通工具类 class Traffic { // 乘客的容量 public int Capacity { get; set;} } // 汽车类 class Car : Traffic { public void Move() { } } // 飞机类 class Plane : Traffic { public void Fly() { } }
父类与子类的构造过程
当构造出一辆汽车时,这个世界也多了一个交通工具,但这个交通工具与这辆汽车是同一件东西。所以当构造一个子类实例的时候同时也会构造一个父类的实例,而且他们是同一个实例。
- 构造子类实例时会先构造父类。换句话说,调用子类构造函数时会先调用父类构造函数。
- 可以使用base关键字调用父类构造函数。
如果没有调用父类构造函数,将会隐式调用父类无参构造函数。
// 交通工具类 class Traffic { public Traffic() { Console.WriteLine("构造了一个交通工具。"); } } // 汽车类 class Car : Traffic { public Car() // 相当于:public Car() : base() { Console.WriteLine("构造了一辆汽车。"); } } class Program { public static void Main() { Car car = new Car(); /* *输出: *构造了一个交通工具。 *构造了一辆汽车。 */ } }
// 交通工具类 class Traffic { // 乘客的容量 public int Capacity { get; set;} public Traffic(int capacity) { this.Capacity = capacity; Console.WriteLine("构造了一个交通工具。"); } } // 汽车类 class Car : Traffic { //public Car() // ERROR: 父类没有无参构造函数,必需显式调用父类构造函数。 //{ //} public Car() : base(5) // OK { Console.WriteLine("构造了一辆汽车。"); } }
父类与子类间的转化
- 如果A是B,就一定可以把A赋值给B。这时A会隐式转换为B。
- 如果A不是B,就一定不能把A赋值给B。
- 如果A可能是B,就可以把A显式转换为B类型然后赋值给B。
Car car = new Car(); // 实例化 Traffic traffic = car; // OK: car会隐式转换为Traffic。 Car car1 = (Car)traffic; // OK: traffic可能是Car,显式转换后可以赋值。 Plane plane1 = (Plane)traffic; // ERROR: traffic有可能是Plane,所以编译时语法是正确的;但traffic实际上不是Plane,所以运行时会报错。
成员的访问
- 上例中
car
与traffic
指代同一个实例。上例中就只有一个实例,因为只有一句实例化代码。 car
变量将这个实例看作一辆汽车,而traffic
变量只是将这个实例看作一个交通工具。- 当把这个实例看作一辆汽车时,可以调用汽车类中的成员,当然也包括父类交通工具中的成员;当把这个实例看作一个交通工具时,只能访问交通工具类中的成员。
- 访问成员时,只看变量的数据类型,而不是看实例有没有这个成员。
Car car = new Car(); Traffic traffic = car; // car和traffic是同一实例 car.Capacity = 5; Console.WriteLine(traffic.Capacity); // 输出:5,traffic与car是同一个实例。 // 访问成员时,只看变量的数据类型。 car.Move(); // OK traffic.Move(); // ERROR: Traffic类型没有Move方法。
同名成员
- 子类中允许定义与父类同名的变量。
在类的内部,可以用this和base区分访问的是当前类成员,还是父类成员。
// 交通工具类 class Traffic { public int capacity = 10; } // 汽车类 class Car : Traffic { public int capacity = 5; // 这时Car类有两个capacity同名变量。 public void Test(int capacity) { // 访问参数capacity Console.WriteLine(capacity); // 根据调用时的参数输出 // 访问当前类的capacity成员 Console.WriteLine(this.capacity); // 输出:5 // 访问父类的capacity成员 Console.WriteLine(base.capacity); // 输出:10 } }
类成员可以是变量、属性,还可以是方法。
// 交通工具类 class Traffic { public void Move() { Console.WriteLine("Traffic.Move"); } } // 汽车类 class Car : Traffic { // 这时Car类有两个Move方法 public void Move() { Console.WriteLine("Car.Move"); } public void Test() { Move(); // 输出:Car.Move this.Move(); // 输出:Car.Move base.Move(); // 输出:Traffic.Move } }
在类的外部,通过变量的数据类型可以访问对应的同名成员。
// 交通工具类 class Traffic { public void Move() { Console.WriteLine("Traffic.Move"); } } // 汽车类 class Car : Traffic { public void Move() { Console.WriteLine("Car.Move"); } } class Program { public static void Main() { Car car = new Car(); Traffic traffic = car; car.Move(); // 输出:Car.Move traffic.Move(); // 输出:Traffic.Move } }
base
与this
的用法总结
base
的作用有2个:
- 访问父类中的成员(确切的说是非静态实例成员)。
- 调用父类构造函数。
this
的作用有2个:
- 访问当前类中的成员(确切的说是非静态实例成员)。
- 调用当前类构造函数。