前言:我们都知道面向对象的三大特性:封装,继承,多态。封装和继承对于初学者而言比较好理解,但要理解多态,尤其是深入理解,初学者往往存在有很多困惑,为什么这样就可以?有时候感觉很不可思议,由此,面向对象的魅力体现了出来,那就是多态,多态用的好,可以提高程序的扩展性。常用的设计模式,比如简单工厂设计模式,核心就是多态。
其实多态就是:允许将子类类型的指针赋值给父类类型的指针。也就是同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。如果这边不理解可以先放一放,先看下面的事例,看完之后再来理解这句话,就很容易懂了。
理解多态之前首先要对面向对象的里氏替换原则和开放封闭原则有所了解。
里氏替换原则(Liskov Substitution Principle):派生类(子类)对象能够替换其基类(超类)对象被使用。通俗一点的理解就是“子类是父类”,举个例子,“男人是人,人不一定是男人”,当需要一个父类类型的对象的时候可以给一个子类类型的对象;当需要一个子类类型对象的时候给一个父类类型对象是不可以的!
开放封闭原则(Open Closed Principle):封装变化、降低耦合,软件实体应该是可扩展,而不可修改的。也就是说,对扩展是开放的,而对修改是封闭的。因此,开放封闭原则主要体现在两个方面:对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。对修改封闭,意味着类一旦设计完成,就可以独立完成其工作,而不要对类进行任何修改。
对这两个原则有一定了解之后就能更好的理解多态。
首先,我们先来看下怎样用虚方法实现多态
我们都知道,喜鹊(Magpie)、老鹰(Eagle)、企鹅(Penguin)都是属于鸟类,我们可以根据这三者的共有特性提取出鸟类(Bird)做为父类,喜鹊喜欢吃虫子,老鹰喜欢吃肉,企鹅喜欢吃鱼。
创建基类Bird如下,添加一个虚方法Eat():
/// <summary> /// 鸟类:父类 /// </summary> public class Bird { /// <summary> /// 吃:虚方法 /// </summary> public virtual void Eat() { Console.WriteLine("我是一只小小鸟,我喜欢吃虫子~"); } }
创建子类Magpie如下,继承父类Bird,重写父类Bird中的虚方法Eat():
/// <summary> /// 喜鹊:子类 /// </summary> public class Magpie:Bird { /// <summary> /// 重写父类中Eat方法 /// </summary> public override void Eat() { Console.WriteLine("我是一只喜鹊,我喜欢吃虫子~"); } }
创建一个子类Eagle如下,继承父类Bird,重写父类Bird中的虚方法Eat():
/// <summary> /// 老鹰:子类 /// </summary> public class Eagle:Bird { /// <summary> /// 重写父类中Eat方法 /// </summary> public override void Eat() { Console.WriteLine("我是一只老鹰,我喜欢吃肉~"); } }
创建一个子类Penguin如下,继承父类Bird,重写父类Bird中的虚方法Eat():
/// <summary> /// 企鹅:子类 /// </summary> public class Penguin:Bird { /// <summary> /// 重写父类中Eat方法 /// </summary> public override void Eat() { Console.WriteLine("我是一只小企鹅,我喜欢吃鱼~"); } }
到此,一个基类,三个子类已经创建完毕,接下来我们在主函数中来看下多态是怎样体现的。
static void Main(string[] args) { //创建一个Bird基类数组,添加基类Bird对象,Magpie对象,Eagle对象,Penguin对象 Bird[] birds = { new Bird(), new Magpie(), new Eagle(), new Penguin() }; //遍历一下birds数组 foreach (Bird bird in birds) { bird.Eat(); } Console.ReadKey(); }
运行结果:
由此可见,子类Magpie,Eagle,Penguin对象可以赋值给父类对象,也就是说父类类型指针可以指向子类类型对象,这里体现了里氏替换原则。
父类对象调用自己的Eat()方法,实际上显示的是父类类型指针指向的子类类型对象重写父类Eat后的方法。这就是多态。