赞
踩
知识点:理解继承的概念 、 掌握在程序中实现继承、 掌握 base 和 protected 关键字 、掌握如何调用父类构造方法 、 掌握继承的特性
面向对象有三大特性:封装、继承和多态。我们学习过封装,封装是信息隐藏,我们定义类的过程就是对数据的一种封装,今天我们将重点讨论继承。
继承反映的是类之间的层次关系,生活中就有很多继承的例子,如:我们知道汽车有型号、颜色、排量、油耗等属性,有行驶和刹车方法。而公交车是汽车的一种,除了有汽车基本的属性和方法外,还能载很多人。吊车也是一种汽车,它除了有汽车的特征外,还能吊货。汽车和公交车、吊车之间就是一种继承关系,公交车和吊车继承于汽车。生活中满足“是一种”关系的,基本都属于继承,比如还有:喜剧演员、话剧演员都是一种演员,冰箱、电视、空调都是一种电器,等等。
如果一个类继承于另外一个类,如:公交车类继承于汽车类,我们把汽车类称为父类或基类,把公交车类称为子类或派生类,有时继承也叫派生,可以说汽车类派生出公交车类。
继承能让我们在原有类的基础上进行扩展,派生出新的类,从而最大程度上重用了原有代码。对于类而言,所谓继承就是子类包含父类的数据结构和行为方式,包括字段、属性、方法和事件。尽管在子类本身的定义中没有包含这些定义,但仍然可以使用这些父类成员。在类的继承中,被继承的类叫基类或父类,继承的类叫派生类或子类。子类在继承父类的属性和方法同时,可以定义新的属性和方法。
观察下面学生类和老师类的类图会发现,它们存在重复的属性:姓名、性别和年龄。如果再定义校长和警察类也可能要定义这些属性,这样就存在大量重复代码。
学生、老师都是一种人,人都有姓名、性别、年龄属性,如果将这些共有属性定义到人类中,学生类和老师类来继承人类,就不用再定义这三个属性了,只需要定义子类自己特定的属性,如分数或科目等。在子类中可以直接访问父类的属性和方法。
继承的语法:
- class 子类:父类
- {
- ...
- }
继承的目的:
第一步,新建项目:①在VS中新建控制台应用(.NET Framework),这里默认命名为【ConsoleApp5】,单击【确定】按钮,自动完成创建,并打开项目。②在右侧【解决方案资源管理器中】,右键单击项目名称【ConsoleApp5】,依次点击【添加】→【类】,在弹出的【添加新项】窗口中,给类起一个名字Person.cs,然后点击【添加】。
第二步,编写人类:Person类添加成功后,系统会默认打开Person.cs文件,在该文件里声明三个属性
- namespace ConsoleApp5
- {
- /// <summary>
- /// 人类Person
- /// </summary>
- class Person // 在人类里,声明三个属性
- {
- public string Name { get; set; }
- public int Age { get; set; }
- public string Gender { get; set; }
- }
- }
第三步,添加学生类:在当前项目中添加一个学生类Student,在系统会默认打开的Student.cs文件中,编写学生类相关信息
代码分析:我们在学生类里,并没有声明Name、Age、Gender属性,但是却可以直接调用,就是因为学生类Student继承了人类Person,因此可以使用父类中的成员。
- namespace ConsoleApp5
- {
- /// <summary>
- /// 学生类
- /// </summary>
- class Student : Person // 类名后面加冒号,表示学生类,继承人类
- {
- public float Score { get; set; } //学生类,在人类的基础上,增加一个分数属性
-
- public void StuSayHello() //声明一个方法,进行自我介绍
- {
- Console.WriteLine("大家好,我是学生:{0},性别:{1},我今年{2}岁,这次的考试分数是{3}",
- this.Name, this.Gender, this.Age, this.Score);
- }
- }
- }
第四步,添加教师类:在当前项目中添加一个教师类Teacher,在系统会默认打开的Teacher.cs文件中,编写学生类相关信息
- namespace ConsoleApp5
- {
- /// <summary>
- /// 教师类
- /// </summary>
- class Teacher : Person // 类名后面加冒号,表示教师类,继承人类
- {
- public string Subject { get; set; } //教师类,在人类的基础上,增加一个学科属性
- public void TeacherSayHello() //声明一个方法,进行自我介绍
- {
- Console.WriteLine("大家好,我是{0}老师,性别:{1},我今年{2}岁,我带的科目是{3}",
- this.Name, this.Gender, this.Age, this.Subject);
- }
- }
- }
第五步,在主程序中写代码:在Program.cs文件中的Main方法里,编写代码new一个学生对象,并赋值,调用方法。
代码分析:我们这里省略了初始化教师类,大家练习的时候,可以new一个教师对象,然后初始化,然后调用教师对象的方法。
代码思考:这里在进行对象初始化的时候,代码是不是稍显冗余?如何简化?
- class Program
- {
- static void Main(string[] args)
- {
- Student stu = new Student(); //声明一个学生对象
- //给对象初始化
- stu.Name = "王迪";
- stu.Gender = "女";
- stu.Age = 18;
- stu.Score = 80;
- stu.StuSayHello(); //调用学生对象的StuSayHello()方法,输出自我介绍
- Console.ReadLine();
- }
- }
预览效果:
案例思考:是不是父类中的所有成员(属性、方法、事件),子类都可以继承呢?(可以通过访问修饰符,控制属性或方法被调用的位置)
访问修饰符 | 本类中 | 子类中 | 当前项目 | 项目外 |
private | 是 | 否 | 否 | 否 |
protected | 是 | 是 | 否 | 否 |
internal | 是 | 是 | 是 | 否 |
public | 是 | 是 | 是 | 是 |
提示:在父类中声明4个属性,分别在父类的方法中、子类的方法中、当前Program类的Main方法中、其他项目里,进行测试。
- private int PrivateInt { get; set; }
- protected int protectedInt { get; set; }
- internal int internalInt { get; set; }
- public int publicInt { get; set; }
继承给程序开发带来了许多好处,但有时候程序员也可能不想让一个类被继承,那么可以使用关键字 sealed 定义类(被 sealed 定义的类叫密封类),如: sealed class A{} ,这代表着A类是密封类,如果再定义 B 类继承 A 类:class B:A ,就会出现编译错误。
举例:
我们将示例1中的人类Person使用sealed关键字,sealed class Person{}
那么,在想让学生类Student继承人类Person时,会提示编译错误:“Student”无法从密封类型“Person”派生
C#中base关键字在继承中起到非常重要的作用。它与this关键字相比,this关键字代表当前实例。base关键字代表父类,使用base关键字可以调用父类的构造函数、属性和方法。
当我们调用子类构造方法时,系统会首先调用父类的构造方法,然后再调用子类构造方法。 调用的方式有两种:
在理解这个内容时,先回顾一下我们前面一课介绍的构造方法的分类及使用。
当创建子类的对象时,系统将会调用父类的构造函数和子类的构造函数,构造函数的执行次序是:先执行父类的构造函数,再执行子类的构造函数。
在示例1中,我们没有添加构造方法,为了查看效果,我们在示例2中,分别给父类Person添加一个无参构造方法和子类Student添加一个无参构造方法。
第一步,新建项目:①在VS中新建控制台应用(.NET Framework),这里默认命名为【ConsoleApp5.1】,单击【确定】按钮,自动完成创建,并打开项目。②在右侧【解决方案资源管理器中】,右键单击项目名称【ConsoleApp5.1】,依次点击【添加】→【类】,在弹出的【添加新项】窗口中,给类起一个名字Person.cs,然后点击【添加】。(同示例1的第一步相同)
第二步,编写人类:Person类添加成功后,系统会默认打开Person.cs文件,在该文件里声明三个属性,并声明一个无参构造方法。
- class Person
- {
- public string Name { get; set; } // 在人类里,声明三个属性:姓名、年龄、性别
- public int Age { get; set; }
- public string Gender { get; set; }
- //声明一个无参构造方法(因为系统默认的无参构造方法没有方法体)
- public Person()
- {
- Console.WriteLine("调用父类的无参构造方法"); //让该方法,在控制台输出一句话
- }
- }
第三步,添加学生类:在当前项目中添加一个学生类Student,在系统会默认打开的Student.cs文件中,编写学生类相关信息(
- /// <summary>
- /// 学生类
- /// </summary>
- class Student : Person // 类名后面加冒号,表示学生类,继承人类
- {
- public float Score { get; set; } //学生类,在人类的基础上,增加一个分数属性
-
- public void StuSayHello() //声明一个方法,进行自我介绍
- {
- Console.WriteLine("大家好,我是学生:{0},性别:{1},我今年{2}岁,这次的考试分数是{3}",
- this.Name, this.Gender, this.Age, this.Score);
- }
- public Student() //声明一个无参构造方法(测试效果使用的)
- {
- Console.WriteLine("调用子类的无参构造方法"); //让该方法,在控制台输出一句话
- }
- }
第四步,在主程序中写代码:在Program.cs文件中的Main方法里,编写代码new一个学生对象,并赋值,调用方法。(这里与示例1第五步步骤相同,我们可以直接复制之前代码)
预览效果:(注意对比一下和示例1的区别,我们可以设置断点,看一下程序运行)
案例思考:示例中,父类只声明了一个无参构造方法,如果父类中同时声明一个有参构造方法,那么子类在调用父类构造方法时,会如何调用?
案例修改:我们在当前项目中,在父类Person里,新声明一个有参方法,打开【Person.cs】文件,编写代码,然后预览效果看一下和刚才的效果有什么区别?思考一下当父类中同时有两个构造方法的时候,默认调用哪一个构造方法?
案例总结:隐式调用父类构造方法,没有指定调用父类哪个构造方法时,会调用父类的无参构造方法。
案例延伸:如果想调用父类的有参构造方法,怎么办?
在示例2的基础上修改
第一步:我们在当前项目中,在子类Student里,新声明一个有参方法,打开【Student.cs】文件,编写代码,
代码分析:注意base关键字的使用。(注意:base()调用父类构造函数时,不需要再次指定参数的类型,因为在子类中已经定义了这些参数,在base()中只需指定变量名即可,参数的类型必须和父类中的一致。)
- //声明一个有参构造方法
- public Student(string name,int age,string gender,float score) : base(name,age,gender)
- {
- this.Score = score;
- Console.WriteLine("调用子类的有参构造方法");
- }
第二步:在Program.cs文件中的Main方法里,新new一个学生对象并赋值,调用方法。
预览效果:
案例总结:
1)base(参数列表)中的参数必须和父类构造方法的参数一致,否则可能出现错误,尝试交换 name 和 age 参数位置,将会提示编译错误:
2)如果不使用base关键字,或者使用base关键字但是不传入参数,那么依然调用父类的无参构造方法。
C#属于单继承,即派生类最多只能有一个直接基类,但可以传递继承,即A类派生B类,B类派生C类。派生类会保留所有基类以及传递基类的字段、属性、方法等所有内容。如果要在派生类中隐藏基类内容,可以通过new关键字实现,可以通过base来调用基类的内容。继承的特性主要体现在:
继承的单根性是指子类只能继承一个父类,不能同时继承多个父类。C#中,派生类只能从一个类中继承。C#不支持多重继承,也就是说,儿子只能有一个亲生父亲,不能同时拥有多个亲生父亲。(C#中使用接口技术实现多重继承。)
前面的示例中,我们设置学生类Student继承人类Person,如果我们再让学生类Student继承Program类,那么程序提示编译错误。
继承的关系可以有很多层,派生类从基类那里继承特性,派生类也可以作为其它类的基类。从一个基类派生出来的多层类形成了类的层次结构。就像现实中爷爷、爸爸和儿子的关系。儿子不仅可以访问父类(爸爸)的所有公有属性方法,也可以访问爸爸的父类(爷爷)的所有公有属性和方法,这种关系称为继承传递性。
演示内容:动物类会吃东西,人类继承于动物,那么人类也会吃东西,人类还会思考,学生继承人类,学生会吃东西、会思考还会学习。
第一步,新建项目:①在VS中新建控制台应用(.NET Framework),这里默认命名为【ConsoleApp5.2】,单击【确定】按钮,自动完成创建,并打开项目。②在右侧【解决方案资源管理器中】,右键单击项目名称【ConsoleApp5.2】,依次点击【添加】→【类】,在弹出的【添加新项】窗口中,给类起一个名字Animal.cs,然后点击【添加】。(同示例1的第一步相同)
第二步,编写动物类:Person类添加成功后,系统会默认打开Animal.cs文件,在该文件里声明一个方法Eat()。
第三步,添加人类:在当前项目中添加一个人类Person,在系统会默认打开的Person.cs文件中,在该文件里,Person类继承与Animal类,同时在Person类里,声明三个属性,并声明一个无参构造方法、一个有参构造方法、一个普通方法Think()。
- /// <summary>
- /// 人类Person
- /// </summary>
- class Person : Animal // 类名后面加冒号,表示人类Person,继承动物类Animal
- {
- public string Name { get; set; } // 在人类里,声明三个属性:姓名、年龄、性别
- public int Age { get; set; }
- public string Gender { get; set; }
- public Person()
- {
- Console.WriteLine("\n调用人类Person的,无参构造方法");
- }
- public Person(string name,int age,string gender)
- {
- this.Name = name;
- this.Age = age;
- this.Gender = gender;
- Console.WriteLine("\n调用人类Person的,有参构造方法");
- }
- public void Think()
- {
- Console.WriteLine("\n{0}同学今年{1}岁,性别:{2},在思考,~~~", this.Name,this.Age, this.Gender);
- }
- }
第四步,添加学生类:在当前项目中添加一个学生类Student,在系统会默认打开的Student.cs文件中,在该文件里,Student类继承与Person类,同时在Student类里,声明一个属性,并声明一个无参构造方法、一个有参构造方法、一个普通方法Study()。
- /// <summary>
- /// 学生类
- /// </summary>
- class Student : Person // 类名后面加冒号,表示学生类Student,继承人类Person
- {
- public float Score { get; set; } //学生类,在人类的基础上,增加一个分数属性
- public Student()
- {
- Console.WriteLine("调用Student类的,无参构造方法");
- }
- public Student(string name, int age, string gender,float score) : base(name, age, gender)
- {
- this.Score = score;
- Console.WriteLine("调用Student类的,有参构造方法");
- }
- public void Study() //声明一个方法,进行学习
- {
- Console.WriteLine("{0}同学此次考试{1}分,在学习中……", this.Name, this.Score);
- }
- }
第五步,在主程序中写代码:在Program.cs文件中的Main方法里,编写代码new一个学生对象,并赋值,调用方法。
- static void Main(string[] args)
- {
- Student jack = new Student("jack", 18,"男",88); //声明对象的同时,初始化
- jack.Eat();
- jack.Think();
- jack.Study();
- Console.ReadLine();
- }
预览效果:
案例思考:当我们new一个Student对象的时候,会不会调用Animal类的构造方法?
案例延伸:如果要实现以下效果,程序如何修改?
前面我们学习过泛型集合,知道泛型集合只能保存同一种类型数据,如何在泛型集合中同时保存学生和老师对象呢? 答案是:定义保存它们父类的泛型集合。老师和学生都属于 Person,所以都可以加入到集合中。但是在 访问集合中的对象时,需要对该对象进行类型判断(对象 is 类型),然后在将对象转换为子类类型。
演示内容:动在泛型集合中,同时保存
第一步,新建项目:①在VS中新建控制台应用(.NET Framework),这里默认命名为【ConsoleApp5.3】,单击【确定】按钮,自动完成创建,并打开项目。②在右侧【解决方案资源管理器中】,右键单击项目名称【ConsoleApp5.3】,依次点击【添加】→【类】,在弹出的【添加新项】窗口中,给类起一个名字Person.cs,然后点击【添加】。(同示例1的第一步相同)
第二步,编写人类:Person类添加成功后,系统会默认打开Person.cs文件,在该文件里声明三个属性。
第三步,添加学生类:在当前项目中添加一个学生类Student,在系统会默认打开的Student.cs文件中,编写学生类相关信息
- /// <summary>
- /// 学生类
- /// </summary>
- class Student : Person // 类名后面加冒号,表示学生类,继承人类
- {
- public float Score { get; set; } //学生类,在人类的基础上,增加一个分数属性
- public Student(string name, int age, string gender, float score) : base(name, age, gender)//声明一个有参构造方法,使用base关键字调用父类Person的有参构造方法
- {
- this.Score = score;
- Console.WriteLine("调用子类Student的有参构造方法");
- }
- public Student() { }//声明一个无参构造方法,当我们定义有参构造方法后,系统将不再默认生成无参构造方法
- public void Study()//声明一个方法,输出学习状态
- {
- Console.WriteLine("\n考了{0}分的{1}同学,今年{2}岁,性别:{3},在学习中...",
- this.Score, this.Name,this.Age,this.Gender);
- }
- }
第四步,添加教师类:在当前项目中添加一个教师类Teacher,在系统会默认打开的Teacher.cs文件中,编写学生类相关信息
- /// <summary>
- /// 教师类
- /// </summary>
- class Teacher : Person // 类名后面加冒号,表示教师类,继承人类
- {
- public string Subject { get; set; } //教师类,在人类的基础上,增加一个学科属性
- public void Teach()
- {
- Console.WriteLine("\n教{0}科目的{1}老师,性别:{2},年龄{3}在上课中...", this.Subject,this.Name,this.Gender,this.Age);
- }
- public Teacher(string name, int age, string gender, string subject)//声明一个有参构造方法,没有使用base关键字
- {
- this.Name = name; //由于没有使用base关键词,因此这里要初始化
- this.Age = age;
- this.Gender = gender;
- this.Subject = subject;
- Console.WriteLine("调用子类Teacher的,有参构造方法");
- }
- public Teacher()
- {
- Console.WriteLine("调用子类Teacher的,无参构造方法");
- }
- }
第五步,在主程序中写代码:在Program.cs文件中的Main方法里,编写代码new一个学生对象和一个教师对象,并初始化,声明一个泛型集合,将学生对象和教师对象添加到集合中,遍历输出集合中的数据
- static void Main(string[] args)
- {
- Student student = new Student("jack", 18, "男", 88);//new一个学生对象,并初始化
- List<Person> list = new List<Person>();//new一个泛型集合
- list.Add(student);//将学生对象,添加到泛型集合list中
- Console.WriteLine();//空行
- Teacher teacher = new Teacher("jhon", 30, "男", "C#");//new一个教师对象,并初始化
- list.Add(teacher);//将教师对象,添加到泛型集合list中(注意:此时list中,有两种数据类型)
- foreach (Person person in list)//遍历输出泛型集合中的数据
- {
- if (person is Student)//判断数据类型,如果是Student类型
- {
- ((Student)person).Study();//将数据强制转换为Student类型,并调用Study()方法
- }
- if (person is Teacher)
- {
- ((Teacher)person).Teach();
- }
- }
- Console.ReadLine();
- }
预览效果:
==========这里是结束分割线=============
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。