多态的概念: 让一个对象能够表现出多种状态(类型),即相同类型的对象调用相同的方法却表现出不同行为的现象。
由于可以继承基类的所有成员,子类就都有了相同的行为,但是有时子类的某些行为需要相互区别,子类需要覆写父类中的方法来实现子类特有的行为,这样的技术在面向对象的编程中就是多态。
通常实现多态有三种手段:(1)、虚方法(2)、抽象类(3)、接口
虚方法
虚方法就是使用virtual关键字修饰的方法。只有父类成员声明为virtual或abstract时,才能被子类重写,如果子类想改变虚方法的实现行为(也就是重写虚方法)时,则必须使用override关键字来修饰。
当在已知条件中,可以找到一个父类并且父类知道该怎么去实现行为的时候,我们可以使用虚方法,来实现多态,当父类不知道该怎么去实现行为的时候我们可以用抽象类,来实现多态。
代码示例:
using System; namespace ConsoleApp { class Program { static void Main(string[] args) { //用虚方法来实现 真的鸭子嘎嘎叫 木头鸭子吱吱叫 橡皮鸭子唧唧叫 RealDuck rd = new RealDuck(); //真鸭子 MuDuck md = new MuDuck(); //木头鸭子 XPDuck xd = new XPDuck(); //橡皮鸭子 RealDuck[] arr = new RealDuck[3] { rd, md, xd }; for (int i = 0; i < arr.Length; i++) { //相同类型的对象调用相同的方法表现出不同的行为 arr[i].Bark(); } Console.ReadKey(); } } /// <summary> /// 真鸭子(父类) /// </summary> public class RealDuck { public virtual void Bark() //虚方法 { Console.WriteLine("真的鸭子嘎嘎叫"); } } /// <summary> /// 木头鸭子(子类) /// </summary> public class MuDuck : RealDuck { //子类应该重写父类的方法,以实现自己特有的行为 public override void Bark() { Console.WriteLine("木头鸭子吱吱叫"); } } /// <summary> /// 橡皮鸭子(子类) /// </summary> public class XPDuck : RealDuck { //重写父类方法 public override void Bark() { Console.WriteLine("橡皮鸭子唧唧叫"); } } }
上面代码父类在需要通过子类中表现不同行为的方法,使用virtual关键字将其定义为虚方法,然后在子类中使用override关键字对父类方法进行重写。这样,每个父类对象在调用相同的方法时将表现出不同的行为,这段代码正是多态虚方法的实现。
如果子类还想继续访问父类定义的方法,则可以使用base关键字来完成调用,例如:base.Bark(); //调用父类的方法。
运行结果:
从上面的运行结果可以看出,相同类型的对象调用相同的方法确实表现出了不同的行为,这就是多态精髓的所在。
阻止派生类重写虚成员
用sealed关键字可以防止一个类被其他类继承,同样,也可以使用sealed关键字来阻止派生类重写虚成员。例如:我们希望MuDuck的继承类不再具有扩展Bark方法的行为,则可以使用sealed关键字来停止虚拟继承,如以下代码所示:
/// <summary> /// 木头鸭子(子类) /// </summary> public class MuDuck : RealDuck { public sealed override void Bark() { Console.WriteLine("木头鸭子吱吱叫"); } }
如果按下面的代码,尝试在MuDuck的派生类中重写Bark方法,则会收到“无法对密封成员进行复写”的错误信息。
/// <summary> /// 红木鸭子 /// </summary> public class RedMuDuck : MuDuck { //编译时错误,因为此时Bark在父类MuDuck中被sealed修饰,定位为密封。 public override void Bark() { Console.WriteLine("红木鸭子嘎吱叫"); } }
抽象类
● 抽象类与抽象方法由abstract关键字修饰。
● abstract关键字的使用注意
1、抽象成员必须标记为abstract,并且不能有任何实现。
2、抽象成员必须在抽象类中。
3、抽象类不能被实例化。
4、子类继承抽象类后,必须把父类中的所有抽象成员都重写(实现抽象方法体),除非子类也是一个抽象类,则可以不进行重写。
5、抽象成员的访问修饰符不能是private。
6、抽象类中可以包含实例成员,并且抽象类的实例成员可以不被子类实现。
7、抽象类是有构造函数的,虽然不能被实例化。
8、如果父类的抽象方法中有参数,那么继承这个抽象父类的子类在重写父类的方法的时候必须传入对应的参数。如果抽象父类的抽象方法中有返回值,那么子类在重写这个抽象方法的时候,也必须传入返回值。
问:什么时候应该用虚方法或抽象类?
答:如果父类中的方法有默认的实现,并且父类需要被实例化的时候,可以考虑将父类定义成普通类,使用虚方法来实现多态。如果父类中的方法没有默认实现,父类也不需要被实例化的时候,则可以将该类定义抽象类来实现多态。
代码示例:
using System; namespace ConsoleApp { class Program { static void Main(string[] args) { //使用抽象类来实现多态的示例: //狗狗会叫 猫咪也会叫 小羊也会叫 Animal[] arr = { new Dog(), new Cat(), new sheep() }; for (int i = 0; i < arr.Length; i++) { //相同类型的对象调用相同的方法表现出不同的行为 arr[i].Voice(); } Console.ReadKey(); } } /// <summary> /// 动物基类 /// </summary> public abstract class Animal //抽象类 { public abstract void Voice(); //抽象方法不能写方法体,大括号也不能有。 } /// <summary> /// 狗(子类) /// </summary> public class Dog: Animal { //子类应该重写父类的方法,以实现自己特有的行为 public override void Voice() { Console.WriteLine("小狗汪汪叫~"); } } /// <summary> /// 猫(子类) /// </summary> public class Cat : Animal { public override void Voice() //重写父类方法 { Console.WriteLine("小猫喵喵叫~"); } } /// <summary> /// 羊(子类) /// </summary> public class sheep : Animal { public override void Voice() //重写父类方法 { Console.WriteLine("小羊咩咩叫~"); } } }
运行结果:
文章评论