Object Interfaces (Delphi)

From RAD Studio
Jump to: navigation, search

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.

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 the ancestorInterface 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.

Note: IInterface is equivalent to IUnknown. You should generally use IInterface for platform independent applications and reserve the use of IUnknown for specific programs that include Win32 dependencies.

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 ...
Note: The SysUtils unit provides an overloaded function called Supports that returns true or false when class types and instances support a particular interface represented by a GUID. The Supports function is used in the manner of the Delphi is and as operators. The significant difference is that the Supports function can take as the right operand either a GUID or an interface type associated with a GUID, whereas is and as take the name of a type. For more information about is and as, see Class References.

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.

See Also