Object Interfaces (Delphi)
Go Up to Object Interfaces Index
An object interface, or simply interface, defines methods that can be implemented by a class. Interfaces are declared as classes, but cannot be directly instantiated and do not have their own method definitions. Rather, it is the responsibility of any class that supports an interface to provide implementations for the method of the interface. A variable of an interface type can reference an object whose class implements that interface; however, only methods declared in the interface can be called using such a variable.
Interfaces offer some of the advantages of multiple inheritance without the semantic difficulties. They are also essential for using distributed object models (such as SOAP). Using a distributed object model, custom objects that support interfaces can interact with objects written in C++, Java, and other languages.
Contents
Interface Types
Interfaces, like classes, can be declared only in the outermost scope of a program or unit, not in a procedure or function declaration. An interface type declaration has the form:
type interfaceName = interface (ancestorInterface) ['{GUID}'] memberList end;
- Warning: The
ancestorInterface
and GUID specification are required to support Win32 COM interoperability. If your interface is to be accessed through COM, be sure to specify theancestorInterface
and GUID.
In most respects, interface declarations resemble class declarations, but the following restrictions apply:
- The memberList can include only methods and properties. Fields are not allowed in interfaces.
- Since an interface has no fields, property read and write specifiers must be methods.
- All members of an interface are public. Visibility specifiers and storage specifiers are not allowed. (But an array property can be declared as default.)
- Interfaces have no constructors or destructors. They cannot be instantiated, except through classes that implement their methods.
- Methods cannot be declared as virtual, dynamic, abstract, or override. Since interfaces do not implement their own methods, these designations have no meaning.
Here is an example of an interface declaration:
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;
In some interface declarations, the interface reserved word is replaced by dispinterface.
IInterface and Inheritance
An interface, like a class, inherits all of its ancestors' methods. But interfaces, unlike classes, do not implement methods. What an interface inherits is the obligation to implement methods, an obligation that is passed onto any class supporting the interface.
The declaration of an interface can specify an ancestor interface. If no ancestor is specified, the interface is a direct descendant of IInterface, which is defined in the System unit and is the ultimate ancestor of all other interfaces. On Win32, IInterface declares three methods: QueryInterface, _AddRef, and _Release.
QueryInterface
provides the means to obtain a reference to the different interfaces that an object supports. _AddRef
and _Release
provide lifetime memory management for interface references. The easiest way to implement these methods is to derive the implementing class from the TInterfacedObject
of the System
unit. It is also possible to dispense with any of these methods by implementing it as an empty function; COM objects, however, must be managed through _AddRef
and _Release
.
- Warning:
QueryInterface
,_AddRef
, and_Release
are required to support Win32 COM interoperability. If your interface is to be accessed through COM, be sure to implement these methods.
Interface Identification and GUIDs
An interface declaration can specify a globally unique identifier (GUID), represented by a string literal enclosed in brackets immediately preceding the member list. The GUID part of the declaration must have the form:
['{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}']
where each x is a hexadecimal digit (0 through 9 or A through F). The Type Library editor automatically generates GUIDs for new interfaces. You can also generate GUIDs by pressing Ctrl+Shift+G in the code editor.
A GUID is a 16-byte binary value that uniquely identifies an interface. If an interface has a GUID, you can use interface querying to get references to its implementations.
- Note: GUIDs are only used for COM interoperability.
The TGUID and PGUID types, declared in the System unit, are used to manipulate GUIDs.
type PGUID = ^TGUID; TGUID = packed record D1: Cardinal; D2: Word; D3: Word; D4: array[0..7] of Byte; end;
Supports can be called in either of two ways:
if Supports(Allocator, IMalloc) then ...
or:
if Supports(Allocator, IID_IMalloc) then ...
Calling Conventions for Interfaces
The default calling convention for interface methods is register, but interfaces shared among modules (especially if they are written in different languages) should declare all methods with stdcall. On Win32, you can use safecall to implement methods of dual interfaces.
Interface Properties
Properties declared in an interface are accessible only through expressions of the interface type; they cannot be accessed through class-type variables. Moreover, interface properties are visible only within programs where the interface is compiled.
In an interface, property read and write specifiers must be methods, since fields are not available.
Forward Declarations
An interface declaration that ends with the reserved word interface and a semicolon, without specifying an ancestor, GUID, or member list, is a forward declaration. A forward declaration must be resolved by a defining declaration of the same interface within the same type declaration section. In other words, between a forward declaration and its defining declaration, nothing can occur except other type declarations.
Forward declarations allow mutually dependent interfaces. For example:
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;
Mutually derived interfaces are not allowed. For example, it is not legal to derive IWindow from IControl and also derive IControl from IWindow.