看上面的UML图,我们创建一个抽象的Instrument类,类中有一个抽象方法paly,然后所有的子类都继承这个类并实现paly方法。(若不懂继承,请参照另一篇:OOP之继承那点事)
我们来看一下类的实现:
public abstract class Instrument { public abstract void Play(); } public class Guitor : Instrument { public override void Play() { Console.WriteLine(string.Format("Play {0}", this.GetType().Name)); } } public class Paino : Instrument { public override void Play() { Console.WriteLine(string.Format("Play {0}", this.GetType().Name)); } } public class Violin : Instrument { public override void Play() { Console.WriteLine(string.Format("Play {0}", this.GetType().Name)); } }
在每个子类方法里,我们直接出去字符串。
class Program { static void Main(string[] args) { Guitor g = new Guitor(); g.Play(); Paino p = new Paino(); p.Play(); Violin v = new Violin(); v.Play(); Console.ReadLine(); } }
在Main方法里,我们创建了三个对象,然后分别输出。 这个说在我们还没开始多态之前的写法,好麻烦有木有(木有?蹲角落写100个乐器的paly).那什么是多态呢?
什么是多态:一种形态的多种表现形式(好抽象...)
THINK IN JAVA上写到:多态是消除类型之间的耦合关系(还是很抽象)
直接看例子:
class Program { static void Main(string[] args) { PlayInstrument(new Guitor()); PlayInstrument(new Paino()); PlayInstrument(new Violin()); Console.ReadLine(); } public static void PlayInstrument(Instrument instrument) { instrument.Play(); } }
这样的输出是和上面的例子是一样的,我们改变了什么,我们提供了一个方法,把3个子类的引用放了进去,呀,为什么PlayInstrument的形参是Instrument的对象,这就是多态的关键之处,
我总结了一句话:子类的引用(new Guitor())指向父类的对象(Instrument instrument) ;
static void Main(string[] args) { Instrument instrument = new Guitor(); instrument = new Paino(); instrument = new Violin(); Console.ReadLine(); }
这个好神奇有木有,我初学时间也觉得,这个好神奇,这个这么玩的,让我来带领大家进入神秘的多态空间。
向上转型
static void Main(string[] args) { Guitor gutior = new Guitor(); Instrument instrument = gutior; Console.ReadLine(); }
向上转型,就说把子类的类型转成父类的,这样是安全的,看上面的例子,如果在VS IDE工具中查看,是没有编译报错的,这时为什么呢?我们来看张图。
这个圆我们把他当作堆(heap)来看,(这没有画栈,因为指向讨论向上转型的安全).我们来看一下Guitor里面包含了Instrument的所有的东西(除了构造,如果说你点不出来私有化的东西,你可以尝试用反射,它的确存在) ,这样我们把大块变成小块是不是很安全呢,因为我大块拥有小块里面所有的东西,所以说向上转型是安全的。
注:相反来说,向下转型是危险的,因为小块变成大块,你不可以控制里面的东西。(协变和逆变概念也差不多,我比较喜欢叫成和谐的变->安全,逆天的变->危险)
这样的话,我们把大块转换成小块(Instrument instrument=new Guitor()),我们只能调用到小块的东西了(所以我们在设计的时候,一般会用接口去限定)
接口其实是应该单独拉出来谈的,但是由于接口基本概念不多,只是在我们OO设计中,接口用处很大,这里属于设计范围,在多态中,用接口做父类和用类写法一样的,可以去试试。
总结一下:
1.向上转型以后,虽然引用还是原本的子类,但是只能用父类的方法了(想想大块变小块)
2.父类方法被子类重写了(virtual,override)以后,向上转型以后,调用相同的方法还是大块的(因为小块的被重写了,这样说感觉很牵强,因为如果我们在子类中用base关键字还是能调用父类的同名方法,至于这块的内存的调用,我一直没找到相关资料,如果有人知道,请分享一下,万分感谢)。
3.接口和抽象类(类)在使用多态的上面是同一个形式
感觉这边写的不好...望大家补充和指出不足,谢谢了。