目录
一、接口类型(Interface Types)
二、接口和继承(IInterface and Inheritance)
三、接口标识和 GUID(Interface Identification and GUIDs)
四、接口调用约定(Calling Conventions for Interfaces)
五、接口属性(Interface Properties)
六、前向声明(Forward Declarations)
对象接口或简称接口,定义了类可以实现的方法。接口被声明为类,但不能直接实例化,也没有自己的方法定义。相反,任何支持接口的类都有责任提供接口方法的实现。接口类型的变量可以引用实现该接口的类的对象;但是,只有接口中声明的方法才能被调用。
接口具有多重继承的一些优点,但没有语义上的困难。对于使用分布式对象模型(如 SOAP)来说,接口也是必不可少的。使用分布式对象模型,支持接口的自定义对象可以与 C++、Java 和其他语言编写的对象进行交互。
接口与类一样,只能在程序或单元的最外层范围内声明,而不能在存储过程或函数声明中声明。接口类型声明的形式如下 :
type interfaceName = interface (ancestorInterface) ['{GUID}'] memberList end;
警告: 为了支持 Win32 COM 的互操作性,需要使用 ancestorInterface 和 GUID 规范。如果要通过 COM 访问接口,请务必指定 ancestorInterface 和 GUID。
在大多数方面,接口声明与类声明相似,但有以下限制:
下面是一个接口声明的示例:
type IMalloc = interface(IInterface)
['{00000002-0000-0000-C000-000000000046}']
function Alloc(Size: Integer): Pointer; stdcall;
function Realloc(P: Pointer; Size: Integer): Pointer; stdcall;
procedure Free(P: Pointer); stdcall;
function GetSize(P: Pointer): Integer; stdcall;
function DidAlloc(P: Pointer): Integer; stdcall;
procedure HeapMinimize; stdcall;
end;
在某些接口声明中,interface 保留字由 dispinterface 代替。
接口与类一样,继承其祖先的所有方法。但接口与类不同,并不实现方法。接口继承的是实现方法的义务,这种义务会传递给任何支持接口的类。
接口的声明可以指定一个祖先接口。如果没有指定祖先接口,该接口就是 IInterface 的直接后代,而 IInterface 是在 System 单元中定义的,是所有其他接口的最终祖先。在 Win32 中,IInterface 声明了三个方法: QueryInterface、_AddRef 和 _Release。
注意:IInterface 等同于 IUnknown。一般来说,独立于平台的应用程序应使用 IInterface,而包含 Win32 依赖性的特定程序则应保留使用 IUnknown。
QueryInterface 提供了获取对象所支持的不同接口引用的方法。_AddRef 和 _Release 为接口引用提供了生命周期内存管理。实现这些方法的最简单方法是从 System 单元的 TInterfacedObject 派生实现类。也可以通过以空函数的形式实现这些方法中的任何一种,但 COM 对象必须通过 _AddRef 和 _Release 进行管理。
警告: 需要使用 QueryInterface、_AddRef 和 _Release 来支持 Win32 COM 互操作性。如果要通过 COM 访问接口,请务必实现这些方法。
接口声明可以指定一个全局唯一标识符(GUID),该标识符由一个字面字符串表示,该字符串括在成员列表前的括号中。声明中的 GUID 部分必须具有以下形式 :
['{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}']
其中每个 x 是十六进制数字(0 到 9 或 A 到 F)。类型库编辑器会自动为新界面生成 GUID。您也可以在代码编辑器中按 Ctrl+Shift+G 生成 GUID。
GUID 是唯一标识接口的 16 字节二进制值。如果接口有 GUID,就可以使用接口查询来获取其实现的引用。
注:GUID 仅用于 COM 的互操作性。
系统单元中声明的 TGUID 和 PGUID 类型用于操作 GUID。
type
PGUID = ^TGUID;
TGUID = packed record
D1: Cardinal;
D2: Word;
D3: Word;
D4: array[0..7] of Byte;
end;
支持有两种调用方式:
if Supports(Allocator, IMalloc) then ...
或者
if Supports(Allocator, IID_IMalloc) then ...
注:SysUtils 单元提供了一个名为 Supports 的重载函数,当类类型和实例支持由 GUID 表示的特定接口时,该函数返回 true 或 false。Supports 函数的使用方式与 Delphi is 和 as 操作符相同。不同之处在于,Supports 函数的右操作数可以是 GUID 或与 GUID 相关联的接口类型,而 is 和 as 操作数的右操作数则是类型名称。有关 is 和 as 的更多信息,请参阅类引用。
接口方法的默认调用约定是 register,但模块间共享的接口(尤其是用不同语言编写的接口)应使用 stdcall 声明所有方法。在 Win32 中,可以使用 safecall 来实现双接口的方法。
接口中声明的属性只能通过接口类型的表达式访问,不能通过类类型变量访问。此外,接口属性只能在编译了接口的程序中可见。
在接口中,属性的读取和写入指定必须是方法,因为字段是不可用的。
以保留字 interface 和分号结尾的接口声明,如果没有指定祖先、GUID 或成员列表,则属于正向声明。前向声明必须由同一类型声明部分中同一接口的定义声明来解决。换句话说,在前向声明和定义声明之间,除了其他类型声明外,不能有任何其他声明。
前向声明允许相互依赖的接口。例如 :
type
IControl = interface;
IWindow = interface
['{00000115-0000-0000-C000-000000000044}']
function GetControl(Index: Integer): IControl;
//. . .
end;
IControl = interface
['{00000115-0000-0000-C000-000000000049}']
function GetWindow: IWindow;
//. . .
end;
不允许相互派生接口。例如,从 IControl 派生 IWindow 和从 IWindow 派生 IControl 都是不合法的。