本篇讲解实现多态的三种方法:虚方法、抽象方法和接口。
人类都能说话,但是不同国家的人可能使用的语言不一样,所以,不同国家的人说同样的话,会有不同的表现形式(同一事情,不同表现形态),如何用程序体现这种差异呢?
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Person[] ps = new Person[5]; 6 ps[0] = new Chinese(); 7 ps[1] = new British(); 8 ps[2] = new Japanese(); 9 10 for (int i = 0; i < ps.Length; i++) 11 { 12 if (ps[i] is Chinese) 13 { 14 Chinese c=(Chinese)ps[i]; 15 c.Introduce(); 16 } 17 18 if (ps[i] is British) 19 { 20 British c = (British)ps[i]; 21 c.Introduce(); 22 } 23 24 if (ps[i] is Japanese) 25 { 26 Japanese c = (Japanese)ps[i]; 27 c.Introduce(); 28 } 29 } 30 31 Console.ReadKey(); 32 } 33 } 34 35 public class Person 36 { 37 public string Name { get; set; } 38 } 39 40 public class Chinese:Person 41 { 42 public void Introduce() 43 { 44 Console.WriteLine("我是中国人"); 45 } 46 } 47 48 public class British : Person 49 { 50 public void Introduce() 51 { 52 Console.WriteLine("我是英国人"); 53 } 54 } 55 56 public class Japanese : Person 57 { 58 public void Introduce() 59 { 60 Console.WriteLine("我是日本人"); 61 } 62 }
上面的代码能体现这种差异,但是会留下其他问题。1.for循环中的if判断会随着数组中的元素增加而增多。2.数组增加一个元素就需要改动以前的代码(添加if判断),违背了程序的开闭原则(对扩展开放,对修改关闭)。如果用虚方法就能避免这个问题。
一、虚方法
下面是虚方法的代码:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Person[] ps = new Person[3]; 6 ps[0] = new Chinese(); 7 ps[1] = new British(); 8 ps[2] = new Japanese(); 9 10 for (int i = 0; i < ps.Length; i++) 11 { 12 //if (ps[i] is Chinese) 13 //{ 14 // Chinese c=(Chinese)ps[i]; 15 // c.Introduce(); 16 //} 17 18 //if (ps[i] is British) 19 //{ 20 // British c = (British)ps[i]; 21 // c.Introduce(); 22 //} 23 24 //if (ps[i] is Japanese) 25 //{ 26 // Japanese c = (Japanese)ps[i]; 27 // c.Introduce(); 28 //} 29 30 ps[i].Introduce();//调用同一个方法,输出不同的结果。对同一事情,作出不同的反应。 31 } 32 33 Console.ReadKey(); 34 } 35 } 36 37 public class Person 38 { 39 public string Name { get; set; } 40 41 public virtual void Introduce() 42 { 43 Console.WriteLine("我是中国人"); 44 } 45 } 46 47 public class Chinese:Person 48 { 49 public override void Introduce() 50 { 51 Console.WriteLine("我是中国人"); 52 } 53 } 54 55 public class British : Person 56 { 57 public override void Introduce() 58 { 59 Console.WriteLine("我是英国人"); 60 } 61 } 62 63 public class Japanese : Person 64 { 65 public override void Introduce() 66 { 67 Console.WriteLine("我是日本人"); 68 } 69 }
用虚方法能避免上面的两个问题。
注意:
1.虚方法的两个关键字:virtual和override。
2.虚方法可以重新,也可以不重写。如果子类有与父类虚方法重名方法,但是又想说明这个方法不是重写的方法,可以加new关键字。
3.用虚方法实现多态后,父类变量传入子类对象,父类变量调用的方法都是父类中已有的方法,只不过实现的方式不一样。如果子类没有重写虚方法(子类中没有显式的写这个方法),此时调用的方法也可以理解为是子类中的,(子类继承父类的),只不过没有显示。
积累:
object 中有ToString(),因为所有的类都继承类object,所以所有的类都有ToString()方法。但是不同的类调用ToString后的结果不同,这取决于这个类中的ToString()方法是怎么实现的。如:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 object obj = new object(); 6 Console.WriteLine(obj.ToString()); 7 8 string[] names = new string[] { "a","b","c"}; 9 10 Console.WriteLine(names.ToString()); 11 12 string agrs = new string(new char[] { 'a', 'b', 'c' }); 13 Console.WriteLine(agrs.ToString()); 14 15 int a = 12; 16 Console.WriteLine(a.ToString()); 17 18 Console.ReadKey(); 19 } 20 }
输出的结果依次是:System.Object,System.String[],abc,12。反编译后发现,前面两种类型的ToString()方法中是返回this.GetType().ToString().后两者返回的结果最终都是this,即当前对象。
二、抽象方法
用抽象方法实现文章开头的问题的代码:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Person[] ps = new Person[2]; 6 ps[0] = new Chinese(); 7 ps[1] = new Japanese(); 8 9 for (int i = 0; i < ps.Length; i++) 10 { 11 ps[i].Introduce(); 12 } 13 14 Console.ReadKey(); 15 } 16 } 17 18 public abstract class Person 19 { 20 public abstract void Introduce(); 21 } 22 23 public class Chinese : Person 24 { 25 public override void Introduce() 26 { 27 Console.WriteLine("我是中国人"); 28 } 29 } 30 31 public class Japanese : Person 32 { 33 public override void Introduce() 34 { 35 Console.WriteLine("我是日本人!"); 36 } 37 }
用虚方法和用抽象方法实现多态的区别
1.虚方法必须有方法体,抽象方法没有。
2.子类可以实现虚方法,也可以不实现。但是抽象方法必须实现(除非子类也是抽象类 )。可见,用抽象方法实现多态,父类对子类控制得更严格。
虚方法和抽象方法实现多态的场景
1.如果子类的实现方式都不一样,那么父类就没必要存在方法体,考虑用抽象方法。
2.如果父类没必要实例化,考虑用抽象方法。
三、接口
1.接口是一种规范,是一种能力。
2.接口中只能包含方法(属性,事件,索引本质上都是方法)
3.能实现多态,代码如下:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 IFlyable supperMan = new SupperMan(); 6 supperMan.Fly(); 7 IFlyable spiderMan = new SpiderMan(); 8 spiderMan.Fly(); 9 Console.ReadKey(); 10 } 11 } 12 13 public interface IFlyable 14 { 15 void Fly(); 16 } 17 18 public class SupperMan : IFlyable 19 { 20 public void Fly() 21 { 22 Console.WriteLine("超人会飞……"); 23 } 24 } 25 26 public class SpiderMan : IFlyable 27 { 28 public void Fly() 29 { 30 Console.WriteLine("蜘蛛侠也会飞……"); 31 } 32 }
上面的代码可用抽象方法实现,为何还用接口实现?如果现在我再加个鸟类,也实现了飞的方法,但是鸟,蜘蛛侠,超人却无法抽象出一个父类,此时可以考虑用接口达到这个目的。
总结:
虚方法,抽象方法,接口都能实现多态,那各自的应用场景是什么呢?
1.虚方法中的方法必须有方法体,子类可以实现。
2.抽象方法不用实现,子类必须显式的实现。当子类的相同动作,实现的方式不同的时候,可以在父类中定义抽象方法(因为父类不用实现,事实上父类中的方法也不知道怎么实现)。当然也可以用接口。
3.当多个类都需要实现某个动作,但是这几个类却无法抽象出一个共同的父类的时候,可以用接口实现。类可以实现多个接口,但是只能继承一个父类,接口可以解决继承的单根性问题。