面向对象3大特性之一:继承
继承
继承表示一个类型(子类)派生自(继承于)一个基类型(父类),该类型将拥有基类型的所有成员字段和函数(不包括构造函数和析构函数)。
注意:
- 派生类继承基类的所有成员,不管是public、private、protected还是其他的,不管是静态的还是非静态的;但是,虽然派生类继承类基类所有的成员,基类却只能访问由public和protected修饰的成员(派生类继承了基类所有的字段,但是只能访问基类中public和protected修饰的成员)
1 class Person 2 { 3 private string name = "张三"; 4 protected int age = 20; 5 public bool gender = true; 6 7 public void ShowInfo_Person() 8 { 9 Console.WriteLine(this.name + ":" + this.age + ":" + this.gender); 10 } 11 } 12 13 class Chinese : Person 14 { 15 public void ShowInfo_Chinese() 16 { 17 Chinese chinese = new Chinese(); 18 Console.WriteLine(chinese.age); 19 Console.WriteLine(chinese.gender); 20 //Console.WriteLine(chinese.name);//由于基类中name用private修饰,所以在派生类中无法访问 21 } 22 } 23 24 class Program 25 { 26 static void Main(string[] args) 27 { 28 Chinese chinese = new Chinese(); 29 chinese.ShowInfo_Person();//输出结果:张三:20:True 此处通过派生类对象chinese可以调用基类中的ShowInfo_Person()方法,可以说明派生类也继承了private修饰的name字段 30 Console.ReadKey(); 31 } 32 }
- 派生类(子类)只能有一个直接基类(父类);但是继承是可以传递的,例如ClassB派生自ClassA,同时ClassC派生自ClassB,那么ClassC会继承ClassB和ClassA中声明的成员。
1 class Animal 2 { 3 public void Method_Animal() 4 { 5 Console.WriteLine("Animal:---------------"); 6 } 7 } 8 9 class Dog : Animal 10 { 11 public void Method_Dog() 12 { 13 Console.WriteLine("Dog:***************"); 14 } 15 } 16 17 class ErHa : Dog 18 { 19 public void Method_ErHa() 20 { 21 Console.WriteLine("ErHa:..............."); 22 } 23 } 24 25 class Program 26 { 27 static void Main(string[] args) 28 { 29 ErHa eh = new ErHa(); 30 eh.Method_Animal(); 31 eh.Method_Dog(); 32 eh.Method_ErHa(); 33 Console.ReadKey(); 34 } 35 }
- C#不支持多重继承,派生类只能有一个直接基类,但是可以实现多个接口,并通过实现多个接口达到多重继承的目的
1 interface IAnimal 2 { 3 void Method_Animal(); 4 } 5 6 interface IDog 7 { 8 void Method_Dog(); 9 } 10 //实现多重继承 11 class ErHa : IAnimal, IDog 12 { 13 public void Method_ErHa() 14 { 15 Console.WriteLine("ErHa:..............."); 16 } 17 18 public void Method_Animal() 19 { 20 Console.WriteLine("Animal:************"); 21 } 22 23 public void Method_Dog() 24 { 25 Console.WriteLine("Dog:---------------"); 26 } 27 } 28 29 class Program 30 { 31 static void Main(string[] args) 32 { 33 ErHa eh = new ErHa(); 34 eh.Method_Animal(); 35 eh.Method_Dog(); 36 eh.Method_ErHa(); 37 Console.ReadKey(); 38 } 39 }
- 结构不支持继承
- 派生类是对基类的扩展,派生类可以对基类进行成员的添加、重写、覆盖操作
构造函数
- 派生类无法继承基类的构造函数
- 当调用派生类的构造函数时,默认先调用基类的无参构造函数,当基类没有无参构造,需要派生类通过base关键字指定要调用的基类构造函数
1 class Person 2 { 3 public Person()
4 { 5 Console.WriteLine("父类的无参构造函数被调用"); 6 } 7 } 8 9 class Student:Person
10 { 11 public Student() 12 { 13 Console.WriteLine("子类的无参构造函数被调用"); 14 } 15 } 16 17 class Program 18 { 19 static void Main(string[] args) 20 { 21 Student stu = new Student(); 22 Console.ReadKey(); 23 } 24 }
执行结果:
当基类中不存在无参的构造函数时,需要通过base关键字派生类指定其要调用的基类的构造函数
1 class Person 2 { 3 public Person(string name) 4 { 5 Console.WriteLine(name); 6 } 7 } 8 9 class Student : Person 10 { 11 public Student() 12 { 13 Console.WriteLine(); 14 } 15 } 16 17 class Program 18 { 19 static void Main(string[] args) 20 { 21 Student stu = new Student(); 22 Console.ReadKey(); 23 } 24 }
以上代码执行时会出现以下错误:
如果我们想要使派生类调用基类的有参构造函数,而不是无参构造函数,就可以在派生类中通过base为其要调用的基类构造函数
1 class Person 2 { 3 public Person(string name) 4 { 5 Console.WriteLine("姓名:"+name); 6 } 7 } 8 9 class Student : Person 10 { 11 public Student(string name, string num) 12 : base(name) 13 { 14 Console.WriteLine("学号:"+num); 15 } 16 } 17 18 class Program 19 { 20 static void Main(string[] args) 21 { 22 Student stu = new Student("张三", "0002"); 23 Console.ReadKey(); 24 } 25 }
接下来我们通过以下代码,了解一下子类对象的初始化过程
1 class Person 2 { 3 public string name = "默认值:张三"; 4 public string gender = "默认值:男"; 5 public Person(string name, string gender) 6 { 7 this.name = name; 8 this.gender = gender; 9 } 10 } 11 12 class Student : Person 13 { 14 public string num = "默认值:0002"; 15 public Student(string name, string gender, string num) 16 : base(name, gender) 17 { 18 this.num = num; 19 } 20 } 21 22 class Program 23 { 24 static void Main(string[] args) 25 { 26 Student stu = new Student("李四", "女", "0004"); 27 Console.WriteLine(stu.name); 28 Console.WriteLine(stu.gender); 29 Console.WriteLine(stu.num); 30 Console.ReadKey(); 31 } 32 }
当在Main方法中实例化Student的对象时,首先在内存中分配了空间,内存中的初始状态如下图:
调用派生类的构造函数,首先会调用基类的构造函数,由于我们在派生类Student中通过base关键字指定了派生类要调用的基类构造函数为public Person(string name,string gender),所以首先会用传入的参数替换掉基类中成员的默认值,执行完成后,内存中的情况如下:
执行完指定的基类构造函数后,执行派生类自身的构造函数,执行完成后,内存中的情况如下:
至此,派生类对象实例化完成。