当前位置:   article > 正文

【C#面向对象】第五课——类的继承、调用父类的构造方法、继承的特性_c#子类继承父类的构造方法

c#子类继承父类的构造方法

知识点:理解继承的概念 、 掌握在程序中实现继承、 掌握 base 和 protected 关键字 、掌握如何调用父类构造方法 、 掌握继承的特性

 

1、理解继承

          面向对象有三大特性:封装、继承和多态。我们学习过封装,封装是信息隐藏,我们定义类的过程就是对数据的一种封装,今天我们将重点讨论继承。

          继承反映的是类之间的层次关系,生活中就有很多继承的例子,如:我们知道汽车有型号、颜色、排量、油耗等属性,有行驶和刹车方法。而公交车是汽车的一种,除了有汽车基本的属性和方法外,还能载很多人。吊车也是一种汽车,它除了有汽车的特征外,还能吊货。汽车和公交车、吊车之间就是一种继承关系,公交车和吊车继承于汽车。生活中满足“是一种”关系的,基本都属于继承,比如还有:喜剧演员、话剧演员都是一种演员,冰箱、电视、空调都是一种电器,等等。

          如果一个类继承于另外一个类,如:公交车类继承于汽车类,我们把汽车类称为父类或基类,把公交车类称为子类或派生类,有时继承也叫派生,可以说汽车类派生出公交车类。

 

1.1   程序中的继承

          继承能让我们在原有类的基础上进行扩展,派生出新的类,从而最大程度上重用了原有代码。对于类而言,所谓继承就是子类包含父类的数据结构和行为方式,包括字段、属性、方法和事件。尽管在子类本身的定义中没有包含这些定义,但仍然可以使用这些父类成员。在类的继承中,被继承的类叫基类或父类,继承的类叫派生类或子类。子类在继承父类的属性和方法同时,可以定义新的属性和方法。

          观察下面学生类和老师类的类图会发现,它们存在重复的属性:姓名、性别和年龄。如果再定义校长和警察类也可能要定义这些属性,这样就存在大量重复代码。

   

         学生、老师都是一种人,人都有姓名、性别、年龄属性,如果将这些共有属性定义到人类中,学生类和老师类来继承人类,就不用再定义这三个属性了,只需要定义子类自己特定的属性,如分数或科目等。在子类中可以直接访问父类的属性和方法。

 

继承的语法:

  1. class 子类:父类
  2. {
  3. ...
  4. }

继承的目的:

  • 提高代码的重用性;
  • 提高程序设计的效率;
  • 为程序设计中的特别需要,提供了编写代码的自由空间,从而提高了已有程序设计成果的可扩展性。

 

示例练习1:使用继承

第一步,新建项目:①在VS中新建控制台应用(.NET Framework),这里默认命名为【ConsoleApp5】,单击【确定】按钮,自动完成创建,并打开项目。②在右侧【解决方案资源管理器中】,右键单击项目名称【ConsoleApp5】,依次点击【添加】→【类】,在弹出的【添加新项】窗口中,给类起一个名字Person.cs,然后点击【添加】。

第二步,编写人类:Person类添加成功后,系统会默认打开Person.cs文件,在该文件里声明三个属性

  1. namespace ConsoleApp5
  2. {
  3. /// <summary>
  4. /// 人类Person
  5. /// </summary>
  6. class Person // 在人类里,声明三个属性
  7. {
  8. public string Name { get; set; }
  9. public int Age { get; set; }
  10. public string Gender { get; set; }
  11. }
  12. }

第三步,添加学生类:在当前项目中添加一个学生类Student,在系统会默认打开的Student.cs文件中,编写学生类相关信息

代码分析:我们在学生类里,并没有声明Name、Age、Gender属性,但是却可以直接调用,就是因为学生类Student继承了人类Person,因此可以使用父类中的成员。

  1. namespace ConsoleApp5
  2. {
  3. /// <summary>
  4. /// 学生类
  5. /// </summary>
  6. class Student : Person // 类名后面加冒号,表示学生类,继承人类
  7. {
  8. public float Score { get; set; } //学生类,在人类的基础上,增加一个分数属性
  9. public void StuSayHello() //声明一个方法,进行自我介绍
  10. {
  11. Console.WriteLine("大家好,我是学生:{0},性别:{1},我今年{2}岁,这次的考试分数是{3}",
  12. this.Name, this.Gender, this.Age, this.Score);
  13. }
  14. }
  15. }

第四步,添加教师类:在当前项目中添加一个教师类Teacher,在系统会默认打开的Teacher.cs文件中,编写学生类相关信息

  1. namespace ConsoleApp5
  2. {
  3. /// <summary>
  4. /// 教师类
  5. /// </summary>
  6. class Teacher : Person // 类名后面加冒号,表示教师类,继承人类
  7. {
  8. public string Subject { get; set; } //教师类,在人类的基础上,增加一个学科属性
  9. public void TeacherSayHello() //声明一个方法,进行自我介绍
  10. {
  11. Console.WriteLine("大家好,我是{0}老师,性别:{1},我今年{2}岁,我带的科目是{3}",
  12. this.Name, this.Gender, this.Age, this.Subject);
  13. }
  14. }
  15. }

第五步,在主程序中写代码:在Program.cs文件中的Main方法里,编写代码new一个学生对象,并赋值,调用方法。

代码分析:我们这里省略了初始化教师类,大家练习的时候,可以new一个教师对象,然后初始化,然后调用教师对象的方法。

代码思考:这里在进行对象初始化的时候,代码是不是稍显冗余?如何简化?

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. Student stu = new Student(); //声明一个学生对象
  6. //给对象初始化
  7. stu.Name = "王迪";
  8. stu.Gender = "女";
  9. stu.Age = 18;
  10. stu.Score = 80;
  11. stu.StuSayHello(); //调用学生对象的StuSayHello()方法,输出自我介绍
  12. Console.ReadLine();
  13. }
  14. }

预览效果:

案例思考:是不是父类中的所有成员(属性、方法、事件),子类都可以继承呢?(可以通过访问修饰符,控制属性或方法被调用的位置)

访问修饰符本类中子类中当前项目项目外
private
protected
internal
public

提示:在父类中声明4个属性,分别在父类的方法中、子类的方法中、当前Program类的Main方法中、其他项目里,进行测试。

  1. private int PrivateInt { get; set; }
  2. protected int protectedInt { get; set; }
  3. internal int internalInt { get; set; }
  4. public int publicInt { get; set; }

 

1.2  sealed关键字

           继承给程序开发带来了许多好处,但有时候程序员也可能不想让一个类被继承,那么可以使用关键字 sealed 定义类(被 sealed 定义的类叫密封类),如: sealed class A{} ,这代表着A类是密封类,如果再定义 B 类继承 A 类:class B:A ,就会出现编译错误。

举例:

我们将示例1中的人类Person使用sealed关键字,sealed  class Person{}

那么,在想让学生类Student继承人类Person时,会提示编译错误:“Student”无法从密封类型“Person”派生

 

1.3   base关键字

           C#中base关键字在继承中起到非常重要的作用。它与this关键字相比,this关键字代表当前实例。base关键字代表父类,使用base关键字可以调用父类的构造函数、属性和方法。

  • 使用base关键字访问父类成员,语法:base.属性名 = 值;
  • 使用base关键字调用父类构造函数的语法如下:子类构造函数:base(参数列表)
  • 使用base关键字调用父类方法的语法如下:base.父类方法();

 

2、子类调用父类的构造方法

          当我们调用子类构造方法时,系统会首先调用父类的构造方法,然后再调用子类构造方法。 调用的方式有两种:

  •   隐式调用父类构造方法,没有指定调用父类哪个构造方法时,会调用父类无参构造方法。
  •   通过 base 关键字显式调用父类构造方法。

在理解这个内容时,先回顾一下我们前面一课介绍的构造方法的分类及使用

 

2.1  子类隐式调用父类的构造方法

          当创建子类的对象时,系统将会调用父类的构造函数和子类的构造函数,构造函数的执行次序是:先执行父类的构造函数,再执行子类的构造函数

          在示例1中,我们没有添加构造方法,为了查看效果,我们在示例2中,分别给父类Person添加一个无参构造方法和子类Student添加一个无参构造方法。

 

示例练习2:隐式调用父类构造方法

第一步,新建项目:①在VS中新建控制台应用(.NET Framework),这里默认命名为【ConsoleApp5.1】,单击【确定】按钮,自动完成创建,并打开项目。②在右侧【解决方案资源管理器中】,右键单击项目名称【ConsoleApp5.1】,依次点击【添加】→【类】,在弹出的【添加新项】窗口中,给类起一个名字Person.cs,然后点击【添加】。(同示例1的第一步相同)

第二步,编写人类:Person类添加成功后,系统会默认打开Person.cs文件,在该文件里声明三个属性,并声明一个无参构造方法。

  1. class Person
  2. {
  3. public string Name { get; set; } // 在人类里,声明三个属性:姓名、年龄、性别
  4. public int Age { get; set; }
  5. public string Gender { get; set; }
  6. //声明一个无参构造方法(因为系统默认的无参构造方法没有方法体)
  7. public Person()
  8. {
  9. Console.WriteLine("调用父类的无参构造方法"); //让该方法,在控制台输出一句话
  10. }
  11. }

第三步,添加学生类:在当前项目中添加一个学生类Student,在系统会默认打开的Student.cs文件中,编写学生类相关信息(

  1. /// <summary>
  2. /// 学生类
  3. /// </summary>
  4. class Student : Person // 类名后面加冒号,表示学生类,继承人类
  5. {
  6. public float Score { get; set; } //学生类,在人类的基础上,增加一个分数属性
  7. public void StuSayHello() //声明一个方法,进行自我介绍
  8. {
  9. Console.WriteLine("大家好,我是学生:{0},性别:{1},我今年{2}岁,这次的考试分数是{3}",
  10. this.Name, this.Gender, this.Age, this.Score);
  11. }
  12. public Student() //声明一个无参构造方法(测试效果使用的)
  13. {
  14. Console.WriteLine("调用子类的无参构造方法"); //让该方法,在控制台输出一句话
  15. }
  16. }

第四步,在主程序中写代码:在Program.cs文件中的Main方法里,编写代码new一个学生对象,并赋值,调用方法。(这里与示例1第五步步骤相同,我们可以直接复制之前代码)

预览效果:(注意对比一下和示例1的区别,我们可以设置断点,看一下程序运行)

案例思考:示例中,父类只声明了一个无参构造方法,如果父类中同时声明一个有参构造方法,那么子类在调用父类构造方法时,会如何调用?

案例修改:我们在当前项目中,在父类Person里,新声明一个有参方法,打开【Person.cs】文件,编写代码,然后预览效果看一下和刚才的效果有什么区别?思考一下当父类中同时有两个构造方法的时候,默认调用哪一个构造方法?

案例总结:隐式调用父类构造方法,没有指定调用父类哪个构造方法时,会调用父类的无参构造方法。

案例延伸:如果想调用父类的有参构造方法,怎么办?

 

2.1  子类通过base关键字显示调用父类的构造方法

          

示例练习3:显示调用父类构造方法

在示例2的基础上修改

第一步:我们在当前项目中,在子类Student里,新声明一个有参方法,打开【Student.cs】文件,编写代码,

代码分析:注意base关键字的使用。(注意:base()调用父类构造函数时,不需要再次指定参数的类型,因为在子类中已经定义了这些参数,在base()中只需指定变量名即可,参数的类型必须和父类中的一致。)

  1. //声明一个有参构造方法
  2. public Student(string name,int age,string gender,float score) : base(name,age,gender)
  3. {
  4. this.Score = score;
  5. Console.WriteLine("调用子类的有参构造方法");
  6. }

第二步:在Program.cs文件中的Main方法里,新new一个学生对象并赋值,调用方法。

预览效果:

案例总结:

1)base(参数列表)中的参数必须和父类构造方法的参数一致,否则可能出现错误,尝试交换 name 和 age 参数位置,将会提示编译错误:

2)如果不使用base关键字,或者使用base关键字但是不传入参数,那么依然调用父类的无参构造方法。

 

 

3、继承的特性

        C#属于单继承,即派生类最多只能有一个直接基类,但可以传递继承,即A类派生B类,B类派生C类。派生类会保留所有基类以及传递基类的字段、属性、方法等所有内容。如果要在派生类中隐藏基类内容,可以通过new关键字实现,可以通过base来调用基类的内容。继承的特性主要体现在:

  • 单根性:子类只能继承一个父类
  • 传递性:父类能够传给子类,也能够传给"孙子"类(可以查看类图)

 

3.1  继承的单根性

           继承的单根性是指子类只能继承一个父类,不能同时继承多个父类。C#中,派生类只能从一个类中继承。C#不支持多重继承,也就是说,儿子只能有一个亲生父亲,不能同时拥有多个亲生父亲。(C#中使用接口技术实现多重继承。)

           前面的示例中,我们设置学生类Student继承人类Person,如果我们再让学生类Student继承Program类,那么程序提示编译错误。

 

3.2  继承的传递性

           继承的关系可以有很多层,派生类从基类那里继承特性,派生类也可以作为其它类的基类。从一个基类派生出来的多层类形成了类的层次结构。就像现实中爷爷、爸爸和儿子的关系。儿子不仅可以访问父类(爸爸)的所有公有属性方法,也可以访问爸爸的父类(爷爷)的所有公有属性和方法,这种关系称为继承传递性。

示例练习4:继承的传递性

演示内容:动物类会吃东西,人类继承于动物,那么人类也会吃东西,人类还会思考,学生继承人类,学生会吃东西、会思考还会学习。

第一步,新建项目:①在VS中新建控制台应用(.NET Framework),这里默认命名为【ConsoleApp5.2】,单击【确定】按钮,自动完成创建,并打开项目。②在右侧【解决方案资源管理器中】,右键单击项目名称【ConsoleApp5.2】,依次点击【添加】→【类】,在弹出的【添加新项】窗口中,给类起一个名字Animal.cs,然后点击【添加】。(同示例1的第一步相同)

第二步,编写动物类:Person类添加成功后,系统会默认打开Animal.cs文件,在该文件里声明一个方法Eat()。

第三步,添加人类:在当前项目中添加一个人类Person,在系统会默认打开的Person.cs文件中,在该文件里,Person类继承与Animal类,同时在Person类里,声明三个属性,并声明一个无参构造方法、一个有参构造方法、一个普通方法Think()。

  1. /// <summary>
  2. /// 人类Person
  3. /// </summary>
  4. class Person : Animal // 类名后面加冒号,表示人类Person,继承动物类Animal
  5. {
  6. public string Name { get; set; } // 在人类里,声明三个属性:姓名、年龄、性别
  7. public int Age { get; set; }
  8. public string Gender { get; set; }
  9. public Person()
  10. {
  11. Console.WriteLine("\n调用人类Person的,无参构造方法");
  12. }
  13. public Person(string name,int age,string gender)
  14. {
  15. this.Name = name;
  16. this.Age = age;
  17. this.Gender = gender;
  18. Console.WriteLine("\n调用人类Person的,有参构造方法");
  19. }
  20. public void Think()
  21. {
  22. Console.WriteLine("\n{0}同学今年{1}岁,性别:{2},在思考,~~~", this.Name,this.Age, this.Gender);
  23. }
  24. }

第四步,添加学生类:在当前项目中添加一个学生类Student,在系统会默认打开的Student.cs文件中,在该文件里,Student类继承与Person类,同时在Student类里,声明一个属性,并声明一个无参构造方法、一个有参构造方法、一个普通方法Study()。

  1. /// <summary>
  2. /// 学生类
  3. /// </summary>
  4. class Student : Person // 类名后面加冒号,表示学生类Student,继承人类Person
  5. {
  6. public float Score { get; set; } //学生类,在人类的基础上,增加一个分数属性
  7. public Student()
  8. {
  9. Console.WriteLine("调用Student类的,无参构造方法");
  10. }
  11. public Student(string name, int age, string gender,float score) : base(name, age, gender)
  12. {
  13. this.Score = score;
  14. Console.WriteLine("调用Student类的,有参构造方法");
  15. }
  16. public void Study() //声明一个方法,进行学习
  17. {
  18. Console.WriteLine("{0}同学此次考试{1}分,在学习中……", this.Name, this.Score);
  19. }
  20. }

第五步,在主程序中写代码:在Program.cs文件中的Main方法里,编写代码new一个学生对象,并赋值,调用方法。

  1. static void Main(string[] args)
  2. {
  3. Student jack = new Student("jack", 18,"男",88); //声明对象的同时,初始化
  4. jack.Eat();
  5. jack.Think();
  6. jack.Study();
  7. Console.ReadLine();
  8. }

预览效果:

案例思考:当我们new一个Student对象的时候,会不会调用Animal类的构造方法?

案例延伸:如果要实现以下效果,程序如何修改?

 

 

4、继承与泛型集合的应用

        前面我们学习过泛型集合,知道泛型集合只能保存同一种类型数据,如何在泛型集合中同时保存学生和老师对象呢? 答案是:定义保存它们父类的泛型集合。老师和学生都属于 Person,所以都可以加入到集合中。但是在 访问集合中的对象时,需要对该对象进行类型判断(对象 is 类型),然后在将对象转换为子类类型。

示例练习5:利用继承,在泛型集合中添加不同数据类型

演示内容:动在泛型集合中,同时保存

第一步,新建项目:①在VS中新建控制台应用(.NET Framework),这里默认命名为【ConsoleApp5.3】,单击【确定】按钮,自动完成创建,并打开项目。②在右侧【解决方案资源管理器中】,右键单击项目名称【ConsoleApp5.3】,依次点击【添加】→【类】,在弹出的【添加新项】窗口中,给类起一个名字Person.cs,然后点击【添加】。(同示例1的第一步相同)

第二步,编写人类:Person类添加成功后,系统会默认打开Person.cs文件,在该文件里声明三个属性。

第三步,添加学生类:在当前项目中添加一个学生类Student,在系统会默认打开的Student.cs文件中,编写学生类相关信息

  1. /// <summary>
  2. /// 学生类
  3. /// </summary>
  4. class Student : Person // 类名后面加冒号,表示学生类,继承人类
  5. {
  6. public float Score { get; set; } //学生类,在人类的基础上,增加一个分数属性
  7. public Student(string name, int age, string gender, float score) : base(name, age, gender)//声明一个有参构造方法,使用base关键字调用父类Person的有参构造方法
  8. {
  9. this.Score = score;
  10. Console.WriteLine("调用子类Student的有参构造方法");
  11. }
  12. public Student() { }//声明一个无参构造方法,当我们定义有参构造方法后,系统将不再默认生成无参构造方法
  13. public void Study()//声明一个方法,输出学习状态
  14. {
  15. Console.WriteLine("\n考了{0}分的{1}同学,今年{2}岁,性别:{3},在学习中...",
  16. this.Score, this.Name,this.Age,this.Gender);
  17. }
  18. }

第四步,添加教师类:在当前项目中添加一个教师类Teacher,在系统会默认打开的Teacher.cs文件中,编写学生类相关信息

  1. /// <summary>
  2. /// 教师类
  3. /// </summary>
  4. class Teacher : Person // 类名后面加冒号,表示教师类,继承人类
  5. {
  6. public string Subject { get; set; } //教师类,在人类的基础上,增加一个学科属性
  7. public void Teach()
  8. {
  9. Console.WriteLine("\n教{0}科目的{1}老师,性别:{2},年龄{3}在上课中...", this.Subject,this.Name,this.Gender,this.Age);
  10. }
  11. public Teacher(string name, int age, string gender, string subject)//声明一个有参构造方法,没有使用base关键字
  12. {
  13. this.Name = name; //由于没有使用base关键词,因此这里要初始化
  14. this.Age = age;
  15. this.Gender = gender;
  16. this.Subject = subject;
  17. Console.WriteLine("调用子类Teacher的,有参构造方法");
  18. }
  19. public Teacher()
  20. {
  21. Console.WriteLine("调用子类Teacher的,无参构造方法");
  22. }
  23. }

第五步,在主程序中写代码:在Program.cs文件中的Main方法里,编写代码new一个学生对象和一个教师对象,并初始化,声明一个泛型集合,将学生对象和教师对象添加到集合中,遍历输出集合中的数据

  1. static void Main(string[] args)
  2. {
  3. Student student = new Student("jack", 18, "男", 88);//new一个学生对象,并初始化
  4. List<Person> list = new List<Person>();//new一个泛型集合
  5. list.Add(student);//将学生对象,添加到泛型集合list中
  6. Console.WriteLine();//空行
  7. Teacher teacher = new Teacher("jhon", 30, "男", "C#");//new一个教师对象,并初始化
  8. list.Add(teacher);//将教师对象,添加到泛型集合list中(注意:此时list中,有两种数据类型)
  9. foreach (Person person in list)//遍历输出泛型集合中的数据
  10. {
  11. if (person is Student)//判断数据类型,如果是Student类型
  12. {
  13. ((Student)person).Study();//将数据强制转换为Student类型,并调用Study()方法
  14. }
  15. if (person is Teacher)
  16. {
  17. ((Teacher)person).Teach();
  18. }
  19. }
  20. Console.ReadLine();
  21. }

预览效果:

 

 

本课总结:

  • 继承能让我们在原有类的基础上进行扩展,派生出新的类,从而最大程度上重用了原有代码。 子类在继承父类的属性和方法同时,可以定义新的属性和方法。
  • 继承的语法是:class 子类:父类
  • 在子类中可以使用 base 关键字来访问父类成员。
  • protected 定义的成员,可以在子类中访问,不能被非子类访问。
  • 系统会首先调用父类的构造方法,然后再调用子类构造方法。
  • 可以通过 base 关键字显式调用父类构造方法。
  • 继承的关系可以有很多层,父类的成员可以一层层传递下去。
  • C#中规定一个类只能继承一个父类。
  • 继承给程序开发带来的好处:
    • 继承是面向对象的重要特性,模拟了现实世界的关系,符合人自然的思考方式。
    • 极大提高了代码重用性。
    • 父类和子类有清晰的结构,子类只用关注于实现自身的特性和行为。

 

 

 

 

==========这里是结束分割线=============

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/138543
推荐阅读
相关标签
  

闽ICP备14008679号