继承和多态
- 继承和多态的基本概念
- 继承
- 继承的类型
- 实现继承
- 接口继承
- 继承
- 派生类
- base关键字
- 构造函数的调用
- 类成员的继承
- 类成员的隐藏
- 继承:虚方法和隐藏方法
- 抽象类和抽象方法
- 抽象类
- 抽象方法
- 密封类和密封方法
- 密封类
- 接口
- 接口成员
- 接口实现
- 多态
- 重载
- 重写
继承和多态的基本概念
继承
-
允许无需重新改写现有类,重用现有类(基类,亦称超类、父类)去创建新类(子类,亦称派生类)
-
C#所有类继承于Object类
-
多态性
对象可以表示多个类型的能力,以自己的方式响应同一消息
继承的类型
实现继承
-
一个类型派生于一个基类型,派生类具有基类的所有非私有(非private)数据和行为
-
派生类型的每个方法采用基类型的实现代码,除非在派生类型的定义中指定重写该方法的实现代码
-
一般用于增加现有类型的功能,或许多相关的类型共享一组重要的公共功能的场合
接口继承
-
一个类型实现若干接口,接口仅包含方法的签名,不继承任何实现代码
-
一般用于指定该类型具有某类可用的特性,在接口中定义通用的实现代码是没有意义的
-
接口即契约,类型派生于接口,即保证该类提供该接口规定的功能
继承
派生类
[类修饰符] class 类名 [: 基类] {类体;}
- 在类名称后放置一个冒号,然后在冒号后指定要从中继承的类(即基类)
- C#的一个派生类只能继承于一个基类
class Point //等同于:class Point : Object
{
int x, y;
}
class ColoredPoint : Point //派生类,从Point派生
{
int color;
}
class Colored3dPoint : ColoredPoint//派生类,从ColoredPoint派生
{
int z;
}
base关键字
用于从派生类中访问基类的成员
base(参数):指定创建派生类实例时应调用的基类构造函数
base.方法(参数):调用基类上已被其他方法重写的方法
base.字段名:访问基类的数据成员
不能从静态方法中使用 base 关键字
只能在实例构造函数、实例方法或实例访问器中使用
public class Person
//等同于:class Person : Object
{
public String name; public int age;
public Person(String name, int age)
//基类构造函数
{
this.name = name; this.age = age;
}
public void print() //基类的实例方法
{
Console.Write("name={0},age={1}", this.name, this.age);
}
}
public class Student : Person //派生类
{
public String studentID;
public Student(String name, int age, String id)
: base(name, age)
//派生类构造函数,使用base调用基类构造函数
{
this.studentID = id;
}
public new void print()
{
base.print();
//使用base调用基类的方法
Console.WriteLine(",studentID={0}", this.studentID);
}
}
构造函数的调用
构造函数不能被继承,也不能被覆盖,名称必须与类名相同
通过关键字new创建对象实例时,会根据传入的参数调用相匹配的构造函数
调用构造函数前,需要先调用其基类的构造函数
类成员的继承
通过继承,派生类继承基类中除构造函数之外的所有成员
与基类成员的访问修饰符无关
类成员的隐藏
如果在派生类中声明了与继承的成员同名的成员,则该重名成员将隐藏从基类继承的同名成员变量
class Parent // 基类Parent
{
public String name;
public String sex;
}
class Child : Parent // 派生类Child
{
public String name;
// 成员变量,隐藏从基类继承的同名成员变量name
public Child(String name, String sex) // 构造函数
{
this.name = name;
//this.name引用Child声明的成员变量
this.sex = sex;
//this.sex引用从Parent继承的成员变量
base.name = name + "'s parent";
//base.name引用从基类Parent继承的同名成员变量name
}
public void print()
{
Console.WriteLine(this.name + " " + this.sex + " " + base.name);
}
}
继承:虚方法和隐藏方法
-
虚方法
在基类中,使用virtual关键字声明的方法 -
隐藏方法
在派生类中,使用new关键字声明的与基类中相同签名的方法
派生类中相同签名方法重新定义了基类中的方法,即隐藏了基类中的同名方法 -
重写方法
在派生类中,使用关键字override声明的与基类中相同签名的方法
- 基类的虚方法声明用于引入新方法
- 派生类的重写方法用于使继承的虚方法专用化
- 虚方法和重写方法主要用于实现多态
抽象类和抽象方法
抽象类
-
将关键字 abstract 置于关键字 class 的前面可以将类声明为抽象类
-
抽象类不能实例化(不能用new),一般用于提供多个派生类可共享的基类的公共定义
-
可以定义抽象类型的变量,但其值必须为 null,或者是其派生的非抽象类的实例的引用
-
允许(但不要求)抽象类包含抽象成员
-
抽象类不能被密封
-
当从抽象类派生非抽象类时,这些非抽象类必须实现所继承的所有抽象成员,从而重写那些抽象成员
抽象方法
抽象类中通过将关键字 abstract 添加到实例方法的返回类型的前面可以定义抽象方法
- 只允许在抽象类中声明抽象方法
- 抽象方法同时隐含为虚方法,但不能有virtual修饰符
- 抽象类的派生类如果是非抽象类,则必须重写抽象基类的所有抽象方法
- 派生类中,不能用base直接引用抽象基类的抽象方法
abstract class Animal
// 基类Animal:抽象类
{
public String name;
public abstract void SayHi();
public Animal(String name)
{ this.name = name; } //构造函数
}
class Dog : Animal // 派生类Dog
{ //重写SayHi()
public override void SayHi() { Console.WriteLine(this.name + " Wow Wow!"); }
public Dog(String name) : base(name) { }
//构造函数
}
class Cat : Animal // 派生类Cat
{ //重写SayHi()
public override void SayHi() { Console.WriteLine(this.name + " Mew Mew!"); }
public Cat(String name) : base(name) { } //构造函数
}
//class Horse : Animal { } //编译错误,非抽象类Horse继承了抽象类Animal,但未实现抽象方法
abstract class Fish : Animal //编译OK,抽象类Fish继承了抽象类Animal,但未实现抽象方法
{
public Fish(String name) : base(name) { }
}
密封类和密封方法
密封类
通过将关键字 sealed 置于关键字 class 的前面,可以将类声明为密封类
- 不能用作基类,也不能是抽象类
- 主要用于防止非有意的派生
- 如果实例方法声明包含sealed 修饰符,则它必须也包含 override 修饰符
- 把类或方法声明为抽象,表示该类或方法必须被重写或继承;而把类或方法声明为密封,表示该类或方法不能重写或继承
接口
接口定义协定
- 接口类似于抽象基类,不能实例化
- 接口只指定实现该接口的类或结构必须提供的成员,不提供实现
- 继承接口的任何非抽象类型必须实现接口的所有成员
- 接口可以包含事件、索引器、方法和属性,但不能包含字段
接口成员
- 接口成员只能包含方法、属性、索引器和事件的声明
- 所有接口成员都隐式地具有public访问属性,不能包含任何修饰符
接口实现
接口可由类和结构实现
[类修饰符] class 类名:基接口列表
{
类体;
}
接口分部与继承
public interface IDimensions //基类:接口IDimensions
{
float getLength(); float getWidth();
}
public class Box : IDimensions //派生类Box
{
float length; float width;
Box(float length, float width)
{
this.length = length; this.width = width;
}
public float getLength() { return length;}
public float getWidth() { return width; }
}
多态
-
派生类对象可以表示多个类型的能力
-
类方法多态性的实现有两种方式:方法重载和方法重写
在这里插入图片描述
重载
class P0 // 基类
{
public void MethodA(){Console.WriteLine("调用类P0的方法methodA()");}
}
class C0 : P0 // 派生类
{
public void MethodA(String str1) //重载方法:重载从基类继承的MethodA()
{
Console.WriteLine("调用类C0的方法MethodA():" + str1);
}
public void MethodB(String str1) //重载方法:重载MethodB(int a)
{
Console.WriteLine("调用C0类的实例方法MethodB(String str1):" + str1);
}
public void MethodB(int a) // 重载方法:重载MethodB(String str1)
{
Console.WriteLine("调用C0类的实例方法MethodB(int a):" + a);
}
}
重写
class A0 // 基类
{
public virtual void MethodA()
{
Console.WriteLine("调用A0的类的实例方法MethodA()");
}
}
class A01 : A0 // 派生类
{
public override void MethodA()//重写方法:重写从基类A0继承的方法
{
Console.WriteLine("调用A01的类的实例方法MethodA()");
}
}
class A02 : A0 // 派生类
{
public override void MethodA()//重写方法:重写从基类A0继承的方法
{
Console.WriteLine("调用A02的类的实例方法MethodA()");
}
}