多态的定义
多种体现形态。
即:一个函数,多种实现。
实现场景
A、重写
A1.父类的引用指向子类的对象
Animal a = new Cat();
a.Speak(); //speak:喵喵
a = new Dog();
a.Speak(); //speak:汪汪
A2.参数定义时:使用父类定义形参;参数传递时:使用子类对象传递实参
class Program
{
Public Void SayHello(Animal a) //使用父类定义形参
{
a.Speak();
}
public static void Main(string[] args) //使用父类定义形参
{
//使用子类对象传递实参
Cat c = new Cat();
Dog d = new Dog();
SayHello(c); //Speak:喵喵
SayHello(d); //Speak:汪汪
}
}
B、重载:XX是一支谁也踢不过的球队。
(传入参数XX为“国足”或者“皇马”时,表达的意思是不一样的)
转型
A、向上转型
Animal a = new Cat(); //把猫提升为动物
B、向下转型
Cat c = (Cat)a;//特别注意:只有在向上转型之后才可以向下转型
a.CathMouse();
多态与前后绑定
A、编译时多态(前绑定)
隐藏(new)是编译时多态。
在编译时就能确定一定是执行父类方法,而非子类方法。
class Animal
{
public virtual void Speak()
{
Console.WriteLine("空气震动,发出声音");
}
}
class Cat:Animal
{
public new void Speak()
{
Console.WriteLine("喵喵");
}
}
B、运行时多态(后绑定)
方法重写(又叫覆盖)(override)是运行时多态。
程序运行时,从实例所属的类开始寻找匹配的方法执行,如果当前类中没有匹配的方法,则沿着继承关系逐层向上,依次在父类或各祖先类中寻找匹配方法,直到Object类。
class Dog:Animal
{
public override void Speak()
{
Console.WriteLine("汪汪");
}
}
class Program
{
public static void Main(string[] args)
{
Animal c = new Cat();
Animal d = new Dog();
c.Speak(); //前绑定--speak:空气震动,发出声音(而不是:喵喵)
d.Speak(); //后绑定--speak:汪汪
}
}
C、前绑定VS后绑定
有的工作在开发时去做,有的则放到运行时去做。
后绑定好处是大大提高程序的复用性和灵活性(便于并行开发),坏处是损耗效率(因为需要逐层去寻找合适的方法,这样做必然会损失效率)。前绑定反之。
C#中的“反射”和“运行时多态”一样,也属于后绑定的设计思想。
多态与设计模式的几大原则
A、多态与里氏替换原则
里氏替换的含义:子类对象能够替换程序中父类对象出现的任何地方,并且保证原来程序的逻辑行为不变及正确性不被破坏。从多态的角度考来考虑,子类只继承(或者实现)父类中提供的方法而不提供多余方法。
举例:因为动物(父类)都会呼吸,所以动物的所有子类(猫、狗、大象)都可以呼吸。
B、多态与依赖倒置原则
依赖倒置的含义:程序要依赖于抽象接口,不要依赖于具体实现。即针对抽象编程。用多态来解释,就是针对“父类”进行编程。
C、多态与接口隔离原则
接口隔离的含义:一个类对另一个类的依赖应该建立在最小的接口上。
接口隔离前(家长类通过人员接口依赖学生类,教育局类通过人员接口依赖老师类,学生类和老师类必须去实现他们不需要的方法):
接口隔离后(把人员接口拆分为三个接口:学生、在校人员、老师):
面向对象的三大特性:多态、封装、继承
不难看出,多态的实现是以封装和继承作为基础的。所以,最后我们再说说:面向对象、封装、继承。
1.面向对象VS面向过程(“Who to do? VS How to do?” 或者 “指挥者VS执行者”)
把大象装进冰箱:
Who:找到一台冰箱(实例化一台冰箱)、找到一个大象管理员(实例化一个大象管理员)
How:打开冰箱、塞进大象、关上冰箱
2.封装
A、便于使用;
B、提高安全性(仅提供所封装代码的功能,而不暴露细节);
C、提高重用性;
D、提高健壮性(在代码里加上权限过滤等);
3.继承
A、提高代码复用性(子类可以直接使用父类中已定义好的方法(protected,public);
B、让类与类之间有了关系,有了这个关系,才有了多态;