Objekt-Interfaces (Object Pascal)

Aus Appmethod Topics
Wechseln zu: Navigation, Suche

Nach oben zu Objekt-Interfaces - Index



Ein Objekt-Interface (oder einfach nur Interface) definiert Methoden, die von einer Klasse implementiert werden können. Interfaces werden wie Klassen deklariert. Sie können aber nicht direkt instantiiert werden und verfügen auch nicht über eigene Methodendefinitionen. Es liegt vielmehr in der Verantwortung der Klasse, von der ein Interface unterstützt wird, für die Implementierung von Interface-Methoden zu sorgen. Eine Variable mit dem Typ Interface kann ein Objekt referenzieren, dessen Klasse das betreffende Interface implementiert. Über diese Variable lassen sich aber nur die Methoden aufrufen, die in dem Interface deklariert sind.

Interfaces bieten einige Vorteile der Mehrfachvererbung, umgehen aber deren semantische Probleme. Außerdem sind sie bei der Verwendung von verteilten Objektmodellen von großer Bedeutung (z. B. SOAP). Benutzerdefinierte Objekte, die Interfaces unterstützen, können dabei mit Objekten interagieren, die mit C++, Java oder anderen Programmiersprachen entwickelt wurden.

Interface-Typen

Interfaces können wie Klassen nur im äußersten Gültigkeitsbereich eines Programms oder einer Unit, nicht aber in einer Prozedur oder Funktion deklariert werden. Die Deklaration eines Interface-Typs sieht folgendermaßen aus:

 type interfaceName = interface (ancestorInterface) ['{GUID}'] memberList end;

Warnung: Das VorfahrInterface und die GUID-Spezifikation sind für die Unterstützung der Win32 COM-Interoperabilität erforderlich. Wenn auf Ihr Interface über COM zugegriffen werden soll, müssen Sie das VorfahrInterface und die GUID angeben.

Eine Interface-Deklaration ähnelt in weiten Teilen einer Klassendeklaration. Es gelten jedoch folgende Einschränkungen:

  • Die MemberListe darf nur Methoden und Eigenschaften enthalten. Felder sind in Interfaces nicht zulässig.
  • Da ein Interface keine Felder hat, müssen die Eigenschaftsbezeichner (read und write) Methoden sein.
  • Alle Member eines Interface sind als public deklariert. Sichtbarkeitsattribute und Speicherattribute sind nicht zulässig. (Eine Array-Eigenschaft kann aber als default deklariert werden.)
  • Interfaces haben keine Konstruktoren oder Destruktoren. Sie können nicht instantiiert werden, ausgenommen durch Klassen, die die Methoden des Interface implementieren.
  • Methoden können nicht als virtual, dynamic, abstract oder override deklariert werden. Da Interfaces keine eigenen Methoden implementieren, haben diese Bezeichnungen keine Bedeutung.

Hier ein Beispiel für eine Interface-Deklaration:

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 manchen Interface-Deklarationen wird das reservierte Wort interface durch dispinterface ersetzt.

IInterface und Vererbung

Ein Interface erbt, wie eine Klasse, alle Methoden seines Vorfahren. Interfaces implementieren aber im Gegensatz zu Klassen keine Methoden. Ein Interface erbt die Verpflichtung zur Implementierung von Methoden. Diese Verpflichtung geht auf alle Klassen über, die das Interface unterstützen.

In der Deklaration eines Interface kann ein Vorfahr-Interface angegeben werden. Wird kein Vorfahr festgelegt, ist das Interface ein direkter Nachkomme von IInterface, das in der Unit System definiert ist und den absoluten Vorfahr aller Interfaces darstellt. In Win32 deklariert IInterface drei Methoden: QueryInterface, _AddRef und _Release.

Hinweis: IInterface entspricht dem Interface IUnknown. Bei der Programmierung plattformunabhängiger Anwendungen sollten Sie jedoch immer IInterface verwenden. Das Interface IUnknown sollte nur in Programmen eingesetzt werden, die Win32-Abhängigkeiten enthalten.

QueryInterface ermöglicht, dass eine Referenz auf die verschiedenen Interfaces, die ein Objekt unterstützt, ermittelt werden kann. _AddRef und _Release sorgen während der Lebensdauer eines Interface für die Verwaltung der Interface-Referenzen. Die einfachste Art, diese Methoden zu implementieren, besteht darin, die implementierende Klasse von TInterfacedObject aus der Unit System abzuleiten. Es ist auch möglich, auf diese Methoden zu verzichten und sie als leere Funktionen zu implementieren. COM-Objekte müssen jedoch immer mit _AddRef und _Release verwaltet werden.

Warnung: QueryInterface, _AddRef und _Release sind zur Unterstützung der Win32 COM-Interoperabilität erforderlich. Wenn auf Ihr Interface über COM zugegriffen werden soll, müssen Sie diese Methoden implementieren.

Identifikation eines Interface und GUIDs

Die Deklaration eines Interface kann einen global eindeutigen Bezeichner (GUID) enthalten, der als String-Literal in eckigen Klammern unmittelbar vor der Member-Liste steht. Der GUID-Abschnitt der Deklaration hat folgende Form:

 ['{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}']

Dabei steht jedes x für eine hexadezimale Ziffer (0 bis 9 und A bis F). Der Typbibliothekseditor generiert automatisch GUIDs für neue Interfaces. Sie können GUIDs aber auch explizit erstellen, indem Sie im Quelltext-Editor die Tastenkombination Strg+Umschalt+G drücken.

Eine GUID ist ein binärer 16-Byte-Wert, der ein Interface eindeutig bezeichnet. Wenn ein Interface eine GUID hat, können Sie über eine Interface-Abfrage Referenzen auf seine Implementierungen abrufen.

Hinweis: GUIDs dienen lediglich der COM-Interoperabilität.

Die Typen TGUID und PGUID, die in der Unit System deklariert sind, werden zur Bearbeitung von GUIDs verwendet.

 type 
     PGUID = ^TGUID;
     TGUID = packed record
       D1: Cardinal;
       D2: Word;
       D3: Word;
       D4: array[0..7] of Byte;
   end;

Supports kann auf zwei Arten aufgerufen werden:

if Supports(Allocator, IMalloc) then ...

oder:

if Supports(Allocator, IID_IMalloc) then ...

Hinweis: Die Unit SysUtils enthält eine überladene Funktion namens Supports, die true oder false zurückgibt, wenn Klassentypen und Instanzen ein bestimmtes von einer GUID repräsentiertes Interface unterstützen. Die Funktion Supports wird wie die Object Pascal-Operatoren is und as verwendet. Ein wichtiger Unterschied ist, dass die Funktion Supports als rechten Operanden entweder eine GUID oder einen einer GUID zugeordneten Interface-Typ übernimmt, während is und as den Namen eines Typs übernehmen. Weitere Informationen über is und as finden Sie unter Klassenreferenzen.

Aufrufkonventionen für Interfaces

Die Standard-Aufrufkonvention für Interface-Methoden ist register. Bei Interfaces, die von verschiedenen Modulen gemeinsam genutzt werden, sollten alle Methoden mit stdcall deklariert werden. Dies gilt insbesondere dann, wenn diese Module in verschiedenen Programmiersprachen erstellt wurden. In der Win32-Programmierung können mit safecall die Methoden von dualen Interfaces implementiert werden.

Interface-Eigenschaften

Auf Eigenschaften, die in einem Interface deklariert werden, kann nur über Ausdrücke vom Typ des Interface zugegriffen werden. Der Zugriff über Variablen vom Typ der Klasse ist nicht möglich. Außerdem sind Interface-Eigenschaften nur in Programmen sichtbar, in denen das Interface compiliert wird.

Da in Interfaces keine Felder verfügbar sind, müssen die Eigenschaftsbezeichner (read und write) Methoden sein.

Vorwärtsdeklarationen

Eine Interface-Deklaration, die mit dem reservierten Wort interface und einem Semikolon endet und keinen Vorfahr, keine GUID und keine Member-Liste enthält, wird als Vorwärtsdeklaration bezeichnet. Eine Vorwärtsdeklaration muss durch eine definierende Deklaration desselben Interface innerhalb desselben Typdeklarationsabschnitts aufgelöst werden. Das bedeutet, dass zwischen einer Vorwärtsdeklaration und ihrer definierenden Deklaration ausschließlich andere Typdeklarationen stehen dürfen.

Vorwärtsdeklarationen ermöglichen voneinander abhängige Interfaces. Zum Beispiel:

 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;

Es ist nicht möglich, Interfaces gegenseitig voneinander abzuleiten. So kann IWindow beispielsweise nicht von IControl und IControl nicht von IWindow abgeleitet werden.

Siehe auch