C#锐利体验 第十讲 接口、继承与多态

第十讲 接口 继承与多态

接口
???????接口定义对象成员的合同,是现代组件编程不可缺少的一环。C#采用关键字“interface”来创建接口。接口作为一种类型,它也具有其他类型所共有的五种存取修饰和new重定义修饰符。接口可以包含方法,属性,事件,索引器四种成员,接口本身只能声明这些成员,不必也不能提供这些成员的具体实现。C#不允许我们为接口成员做任何存取修饰,接口成员的存取修饰缺省为public——接口本来就是我们为其他类型定义的一种成员合同,对他们进行存取限制不符合我们应用接口的初衷。同样的道理,C#也不允许对接口做“abstract, virtual, override, static”修饰。下面是接口类型声明的一个典型的例子:

public delegate void StringListEvent(IStringList sender);
public interface IStringList
{
???void Add(string s);//方法
???int Count { get; }//只读属性
???event StringListEvent Changed;//事件
???string this[int index] { get; set; }//索引器
}??
???????
注意这里我们将接口名称写成IStringList,其中的大写字母“I”仅仅是.NET平台的命名约定,而非必须。
???????接口作为一种用于“合同”的类型不可以象类那样实例化,我们说一个数据是某个接口类型,我们是指该类型实现了某个接口。C#中类或结构可以实现多个接口,实现了接口的类型必需提供相应接口成员的实现。
interface ICloneable
{
???object Clone();
}
interface IComparable
{
???int CompareTo(object other);
}
class ListEntry: ICloneable, IComparable
{
???public object Clone() {...}// ICloneable接口实现
???public int CompareTo(object other) {...}// IComparable接口实现
}??
???????接口之间可以继承,一个接口可以继承自多个接口。直接或间接继承自己都会引起编译时错误。下面是一个接口继承的例子:
interface IControl
{
???void Paint();
}
interface ITextBox: IControl
{
???void SetText(string text);
}
interface IListBox: IControl
{
???void SetItems(string[] items);
}
interface IComboBox: ITextBox, IListBox {}????
???????通过继承,接口IComboBox拥有Paint,SetText,SetItems三个方法合同。多接口继承可能会带来同名成员冲突的问题,C#通过明晰转型来解决这个问题。看下面的例子:
interface IList
{
???int Count { get; set; }
}
interface ICounter
{
???void Count(int i);
}
interface IListCounter: IList, ICounter {}
class C
{
???void Test(IListCounter x) {
??????x.Count(1);????????????????// 错误!命名冲突
??????x.Count = 1;???????????????// 错误!命名冲突
??????((IList)x).Count = 1;?????????// 正确!调用IList.Count.set
??????((ICounter)x).Count(1);??????// 正确!调用ICounter.Count
???}
}

???????注意这种明晰转型是在编译时进行的,并不会带来任何程序运行的代价。上面的例子用IListCounter接口作为方法Test的参数类型,但我们知道接口是不可以实例化的类型,我们该怎样传递这样的参数呢?答案是传递直接或间接实现了该接口的类或结构的对象实例。

继承
???????继承是面向对象的三大特征之一(另外两个是封装和多态),在组件编程中有相当的应用。C#为我们提供两种继承机制——类继承和接口继承。类继承只允许每个类有一个父类(即单继承),而接口继承却允许一个类或接口可以同时继承(又称实现)多个接口。类不可以被接口继承。
在C#中类继承是指通过继承子类将拥有除父类的实例构造器,静态构造器和析构器以外的所有的成员。注意这里的“拥有”和“可见性”是两个概念。拥有某个成员是指该成员确确实实地存在于该类中,但如果该成员的保护级不允许该成员在继承子类中可见(比如private,internal),我们将不能在子类中对他们进行操作——但这不表示他们不存在。下面的例子清晰地说明了这一点:
class A
{
????int count;
????public int Count
????{
????????get {return count;}
????????set {count=value; }
????}
}
class B:A
{}
class Test
{
????public static void Main()
????{
????????B b=new B();
????????b.Count=39;
????????System.Console.WriteLine(b.Count);
????}
}
???????count变量在A中为private,在B中不可见,但通过公有属性Count的存取,我们可以清晰地感觉到它的存在。
子类在继承的同时可以添加新的类成员。new关键字可以使子类在继承的时候屏蔽同名的父类成员,注意这里屏蔽的意思同样是“不可见”,而非“去除”。
我们可以用类修饰关键字abstract和sealed来控制类继承时的行为。abstract使得该类只能被子类继承,而不能被实例化。sealed不允许该类被继承,使得继承树“到此为止”。
接口继承由于所有的成员都为public,它的行为比较简单,仅仅是继承所有接口的成员。和类继承相同的地方是new关键字同样可以屏蔽同名成员。

多态
???????多态是指为同名的方法提供不同的实现的能力,它使得我们不用关心方法的具体实现而仅仅依靠其名称来进行调用操作。比如我们现在有一个Road的类需要调用Drive方法,不管我们有什么样的车,Bike,Car,Jeep,尽管他们的Drive方式不同,但只要他们有Drive方法,我们的Road就可以调用他们的Drive方法使他们在上面“行驶”。多态集中体现了对象世界的共同行为。C#为我们提供了三种多态能力:接口多态,继承多态,抽象多态。
???????接口定义一个类型需要实现的方法,属性,索引和事件,包括可能的参数类型和返回值类型,而把具体的实现交由相应的类或结构来做,从而为组件提供多态能力。接口多态不仅为组件提供一个更好的逻辑功能的聚合方式,也为组件的多版本提供了很好的支持。随着组件规模的不断增长,需要的成员越来越多,将所有的成员简单地封装在一个组件内既不符合我们的逻辑思维方式,更会带来烦恼的版本问题。而将这些成员分解成多个聚合的逻辑块,然后采用接口的形势将他们进行封装,这种方式很好地解决了组件在设计和维护两个方面的问题。接口多态在现代软件开发中有着广泛的应用。
???????类继承使得子类自动拥有父类大多数的成员,C#允许我们通过普通的类继承来为组件提供多态能力。继承常用于在一个现有父类的基础上的功能扩展,往往是我们将几个类中相同的成员提取出来放在父类中实现,然后在各自的子类中加以继承。“子类 is a 父类,苹果 is a水果”是继承多态的经典测试用语。继承对于小规模的软件开发多有裨益,但在现代集群组件开发模式下,由于常常引入晦涩的逻辑误用和不必要的编码负担,它往往不被推荐使用。
???????抽象多态是指通过abstract(抽象)类同时实现继承和接口的多态功能。抽象类中既可以包括实现了的成员,也可以是仅仅提供成员接口而没有具体实现。抽象类不能被实例化,必须在其继成类中实现相应的接口,否则子类也应被标志为abstract。抽象类在为其子类提供一个恒定的功能集的同时,也为其子类提供了一个具备扩展能力的弹性接口。抽象类在组件的初始设计时非常有用,但由于类不能进行多继承,它的多态能力较之接口多态要有所损失。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值