讲不出再见粤语:计算机世界网-周报全文-接口继承与多态

来源:百度文库 编辑:九乡新闻网 时间:2024/04/25 08:30:28

接口

接口定义对象成员的合同,是现代组件编程不可缺少的一环。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();

}

class ListEntry: Icloneable {

public object Clone() {……}

// ICloneable接口实现

}

接口之间可以继承,一个接口可以继承自多个接口。直接或间接继承自己会引起编译时错误。下面是一个接口继承的例子:

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。抽象类在为其子类提供一个规定的功能集的同时,也为其子类提供了一个具备扩展能力的弹性接口。抽象类在组件的初始设计时非常有用,但由于类不能进行多继承,它的多态能力较之接口多态要有所损失。