从Delphi3开始支持接口。接口定义了能够与一个对象进行交互操作的一组过程和函数。对一个接口进行定义包含两个方面的内容:
1)一方面是实现这个接口
2)另一方面是定义接口的客户
一个类能够实现多个接口,即提供多个让客户用来控制对象的“表现方式”
正如名字所表现的,一个接口就是对象和客户通信的接口。这个概念就像C++中的 pure virtual类。实现接口的函数和过程就是支持这个接口的类的工作。
一、定义接口
就像多有的Delphi类都派生于TObject一样,所有的接口都派生于一个称谓IUnKnown的接口,IUnKnown在system单元中定义如下
type IUnKnown = interface ['{00000000-0000-0000-C000-000000000046}'] function QueryInterface(const IID: TGUID; out Obj): Integer; stdcall; function _AddRef: Integer; stdcall; function _Release: Integer; stdcall; end;
接口的定义就像是类的定义,最根本的不同是在接口中有一个全局唯一标识符(GUID),它对应每一个接口而言都是不同的。对于IUnKnown的定义来自于MicroSoft的组件对象模型(COM)
规范。
如果你知道怎么创建Delphi的类,那么定义一个定制的接口是一件简单的事情,下面的代码定义了一个新的接口IFoo,它包含一个称为F1()的方法
type IFoo = interface
['{2137B60-AA33-11D0-A9BF-9A537A42701}'] function F1: Integer; end;
提示:在Delphi的IDE中,按Ctrl+Shift+G快捷键,就可以为接口生成一个新的GUID
下面的代码声明了一个称为IBar的接口,它是从IFoo接口继承来的
type IBar = interface(IFoo) ['{2137BF61-AA33-11D0-A9BF-9A4537A42701}'] function F2: Integer; end;
二、实现接口
下面的代码演示了在一个类TFooBar中怎么实现IFoo和IBar接口
type TFooBar = class(TInterfacedObject, IFoo, IBar) function F1: Integer; function F2: Integer; end; function TFooBar.F1:Integer; begin Result:= 0; end; function TFooBar.F2: Integer; begin Result:= 0; end;
一个类实现接口的时候,一定要在class的括号里面加上这个类:TInterfacedObject
注意,一个类可以实现多个接口,只要在声明这个类的时候,一次列出要实现的接口。编译器通过名称来把接口中的方法与实现接口中的类中的方法对应起来,如果一个类只是声明要实现某个接口,但并没有具体实现这个接口中的方法,编译将会出错
如果一个类要实现多个接口,而这些接口中包含同名方法,必须把同名的方法另取一个别名,例子
type IFoo = interface ['{21313134-DE23-1213-1123-A328301232}'] function F1: Integer; end; IBar = interface ['{21313134-DE23-1213-1123-A32DE01232}'] function F1: integer; end; TFooBar = class(TInterfaceObject, IFoo, IBar) //为同名方法取别名 function IFoo.F1 = FooF1; function IBar.F1 = BarF1; //接口方法 function FooF1: Integer; function BarF1: Integer; end; function TFooBar.FooF1: Integer; begin Result:= 0; end; function TFooBar.BarF1: Integer; begin Result:= 0; end;
三、implements指示符
implements指示符是在Delphi4中引进的,它的作用是委托另一个类或者接口实现接口中的某个方法这个技术有时又被称为委托实现,关于 implements指示符的用法,请看下面的代码:
type TSomeClass = class(TInterfacedObject, IFoo) function GetFoo: TFoo; property Foo: TFoo read GetFoo implements IFoo; end;
在上面的例子中的implements指示符是要求编译器在Foo属性中寻找实现IFoo接口方法。属性的类型必须是一个类,它包含IFoo方法或者类型是IFoo的接口后或者IFoo派生接口。implements指示符后面可以列出几个接口,彼此用逗号隔开
implements指示符在开发中提供了两个好处:
1)首先,它允许以无冲突的方式进行接口聚合。聚合是COM中的概念,它的作用是把多个类合在一起共同完成一个任务。
2)其次,它能够延后占用实现接口所需的资源,知道确实需要资源。例如,假设实现一个接口需要分配一个1MB的位图,但是这个接口很少用到,因此,可能平时你不想实现这个接口,因为它太耗费资源了,用implements指示符之后,可以只在属性被访问时才创建一个类来实现接口
四、使用接口
当在应用程序中使用接口类型的变量时,要用到一些重要的语法规则。最需要记住的是,一个接口是生存期自管理类型的,这就意味着,它通常被初始化为NIL,它是引用计数的,当获得一个接口时自动增加一个引用计数;当它离开作用域或者赋值为NIL时它被自动释放。下面的代码演示一个接口变量的生存期自管理机制
var I : ISomeInterface; begin //I被初始化为NIL I:= FunctionReturningAnInterface; //I的引用计数加1 I.SomeFunc; // I 的引用计数减1,如果为0,则自动释放 end;
关于接口变量的另一个规则是,一个接口变量与实现这个接口的类是赋值兼容的,例如,下面的代码是合法的:
procedure Test(FB: TFooBar) var F:IFoo; begin F:= FB; //合法,因为FB支持IFoo .. ..
最后,类型强制转换运算符as可以把一个接口类型的变量强制类型转换为另一个接口,例子
var FB: TFooBar; F: IFoo; B IBar; begin FB:= TFooBar.Create; F:= FB; //合法,因为FB支持IFoo B:= F as IBar; //合法,把F转换为IBar ..