首先,在C#中,一个类仅可以继承于一个基类(可以是抽象类,也可以是普通的非密封类),但是它可以实现多个接口。因此,当某个类需要同时拥有多种不同的操作时,封装这些操作的类型就不得不被定义为接口。例如,“学生”实体本身可以根据年龄来比较大小,同时它作为一种实体,还需要能够被序列化/反序列化。此时,我们可以让学生实体同时实现ISerializable和IComparable接口以达到这样的效果。严格的来说,并非所有实体都具有可比性,但从技术角度来讲,实体都应该具备序列化/反序列化的能力。因此在这里的案例中,我们应该定义一个IEntity的接口,使其实现ISerializable接口,然后再让学生实体同时实现IEntity和IComparable接口。
其次,基类可以定义更多的信息,当相似种类的对象有着共同的操作和/或属性时,基类可以提供一个默认值,而子类则可以根据需要来选择是否重载/重写这些默认值。这一点对于接口来说是无法办到的。比如上文中我们给电池的正负极一个默认值,直到最后定义“标准镍氢电池”时,我们仍然没有去重载这个值,而是一直沿用了定义在基类中的默认值,这在代码重用上有着重要的意义。不仅如此,当我们修改了基类中某个成员的默认值时,所有继承于该类的子类,如果没有重载这个成员的话,那么它们中相应成员的取值也会随之变化,而这一变化并不需要对子类进行重新编译。
再次,由于值类型都是继承于ValueType,因此,无法再让值类型继承于其它类型,但值类型可以实现多个接口。当某一操作集合需要应用在值类型上时,只能将其封装到接口
至此,基本上对抽象类与接口的使用做了全面的讨论,事实上也并没有一个严格的标准去区分什么时候用抽象类、什么时候用接口。我想,最关键也是最主要的依据仍然是前面一篇文章中所提到的,使用面向对象的语义去区分,而这就需要长期不断的经验积累,才能做出更加准确的理解与判断。
共同学习,写下你的评论
评论加载中...
作者其他优质文章