父类与子类

父类与子类的关系

  • 从编程思想的角度讲,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,所以运行时会报错。
    

成员的访问

  • 上例中cartraffic指代同一个实例。上例中就只有一个实例,因为只有一句实例化代码。
  • 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
      }
    }
    

basethis的用法总结

base的作用有2个:

  • 访问父类中的成员(确切的说是非静态实例成员)。
  • 调用父类构造函数。

this的作用有2个:

  • 访问当前类中的成员(确切的说是非静态实例成员)。
  • 调用当前类构造函数。

results matching ""

    No results matching ""