C#中的继承、多态与接口详解
继承、多态和接口在开发时基本都会用到,考虑到有些人可能对这些还不熟悉,下面就给大家分别介绍下继承、多态和接口,一起来看看吧。
简单继承
最简单的三个类
- public class Animal {
- public Animal()
- {
- Debug.Log("Construct Animal!");
- }
- }
- public class Mammal : Animal {
- public Mammal()
- {
- Debug.Log("Construct Mamal!");
- }
- }
- public class Sheep : Mammal
- {
- public Sheep()
- {
- Debug.Log("Construct Sheep!");
- }
- }
在main里面
- Sheep sheep = new Sheep();
子类的构造函数会依次执行基类的构造函数,没有异议。
需要注意的是,没有特别声明,子类都会自动去找父类中没有参数的构造函数,如果基类中没有,则需要在子类的构造函数中调用,比如:
- public class Animal{
- int age;
- public Animal(int _age)
- {
- age = _age;
- Debug.Log("Construct Animal!");
- }
- }
则子类的构造函数就要这样写 copy
- public Mammal():base(0)
- {
- Debug.Log("Construct Mamal!");
- }
在基类Animal中添加函数 copy
- public void Eat()
- {
- Debug.Log("Animal Eat!");
- }
下面谈一下方法的覆盖
在子类Sheep中覆盖定义Eat方法
- public new void Eat()
- {
- Debug.Log("Sheep Eat!");
- }
这里的new关键字表示隐藏基类中的方法,官方解释如下:
new 关键字可以显式隐藏从基类继承的成员。隐藏继承的成员时,该成员的派生版本将替换基类版本。虽然可以不使用 new 修饰符来隐藏成员,但将收到编译器警告。如果使用 new 来显式隐藏成员,将禁止此警告。
测试代码: copy
- Sheep sheep = new Sheep();
- Animal animal = new Sheep();
- sheep.Eat();
- animal.Eat();
结果是这样的
没有发生多态行为,声明的基类对象,执行的就是基类方法,声明的子类对象,执行的就是子类方法。
在子类的函数中,都有一个base变量,可以认为是一个对基类的引用,通过base就可以调用基类的方法。
多态
多态就是多态就是父类引用指向子类对象,调用方法时会调用子类的实现,而不是父类的实现,这叫多态。
基类Animal中定义函数
- public virtual void Birth()
- {
- Debug.Log("Animal Birth!");
- }
Sheep中overrride一下 copy
- public override void Birth()
- {
- Debug.Log("Sheep Birth!");
- }
测试代码
- Sheep sheep = new Sheep();
- Animal animal = new Sheep();
- animal.Birth();
执行结果
有下面两个需要注意的点
1.子类调用父类的方法
当然是直接用base了。
2.继承链中的虚方法
无论在虚拟成员和最初声明虚拟成员的类之间已声明了多少个类,虚拟成员永远都是虚拟的。如果类 A 声明了一个虚拟成员,类 B 从 A 派生,类 C 从类 B 派生,则类 C 继承该虚拟成员,并且可以选择重写它,而不管类 B 是否为该成员声明了重写。
Sealed关键字的用法
Sealed关键字用来阻止派生类重写虚拟成员。还是举例说明。
基类Animal中定义方法
- public virtual void Move()
- {
- Debug.Log("Animal Move!");
- }
Mammal中sealed override这个函数 copy
- public sealed override void Move()
- {
- Debug.Log("Mammal Move!");
- }
这时候在Sheep中已经无法overide这个函数了,因为在Sheep看来,基类的Move是被当作一个普通函数来处理的,如果要定义自己的Move函数,就要加new,像这样
- public new void Move()
- {
- Debug.Log("Sheep Move!");
- }
写点代码来测试多态性copy
- Animal animal = new Animal(1);
- Animal animal1 = new Mammal(1);
- Animal animal2 = new Sheep();
- Sheep sheep = new Sheep();
- animal.Move();
- animal1.Move();
- animal2.Move();
- sheep.Move();
运行结果
第一个无话可说,直接执行了Animal的move函数
第二个,正常的多态
第三个有点意思,多态到Mammal就打住了
第四个就是普通的执行Sheep类的成员函数。
abstract关键字的使用
官方解释:abstract 修饰符指示所修饰的内容缺少实现或未完全实现。abstract 修饰符可用于类、方法、属性、索引器和事件。在类声明中使用 abstract 修饰符以指示某个类只能是其他类的基类。标记为抽象或包含在抽象类中的成员必须通过从抽象类派生的类来实现。
接地气的解释:abstract类用于搭建子类的框架,子类必须实现其中的abstract方法,使用的也是override关键字。
abstract方法和vitual方法的区别
abstract方法没有函数体,子类一定要有对应方法的实现。
vitual方法子类可以选择实现也可以选择不实现。
两个都可以用于实现多态。
抽象类不能实例化。抽象类的用途是提供一个可供多个派生类共享的通用基类定义。例如,类库可以定义一个抽象类,将其用作多个类库函数的参数,并要求使用该库的程序员通过创建派生类来提供自己的类实现。
接口
接口包含类或结构可以实现的一组相关功能的定义。
例如,使用接口可以在类中包括来自多个源的行为。 由于C#语言不支持多重继承,所以该功能很重要。 此外,如果要模拟结构的继承,也必须使用接口,因为它们无法实际从另一个结构或类继承。
定义一个简单的接口,只有接口函数的定义,没有实现。
- public interface ISay {
- void SayHello();
- void SayFuck();
- }
- public class Sheep : Mammal, ISay
则Sheep类中必须要有ISay中定义的接口的定义
- public void SayHello()
- {
- Debug.Log("ISay Hello");
- }
- public void SayFuck()
- {
- Debug.Log("ISay SayFuck");
- }
测试代码
- Sheep sheep = new Sheep();
- sheep.SayHello();
- sheep.SayFuck();
执行结果
接口有下面几个属性
接口类似于抽象基类。实现接口的任何类或结构都必须实现其所有成员。
接口无法直接进行实例化。其成员由实现接口的任何类或结构来实现。
接口可以包含事件、索引器、方法和属性。
接口不包含方法的实现。
一个类或结构可以实现多个接口。一个类可以继承一个基类,还可实现一个或多个接口。
参考
MSDN