Interfaces implementieren
Nach oben zu Objekt-Interfaces - Index
Inhaltsverzeichnis
Nach der Deklaration muss das Interface in einer Klasse implementiert werden, bevor es verwendet werden kann. Die in einer Klasse implementierten Interfaces werden in der Deklaration der Klasse nach dem Namen der Vorfahrklasse angegeben.
Klassendeklarationen
Die Deklaration sieht folgendermaßen aus:
type className = class (ancestorClass, interface1, ..., interfaceN) memberList end;
Zum Beispiel:
type TMemoryManager = class(TInterfacedObject, IMalloc, IErrorInfo) // ... end;
Hier wird eine Klasse namens TMemoryManager
deklariert, die die Interfaces IMalloc
und IErrorInfo
implementiert. Wenn eine Klasse ein Interface implementiert, muss sie alle in dem Interface deklarierten Methoden implementieren (oder eine Implementierung jeder Methode erben).
Nachstehend sehen Sie die Deklaration von System.TInterfacedObject
(für Windows. Auf anderen Plattformen weicht die Deklaration geringfügig ab):
type TInterfacedObject = class(TObject, IInterface) protected FRefCount: Integer; function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; function _AddRef: Integer; stdcall; function _Release: Integer; stdcall; public procedure AfterConstruction; override; procedure BeforeDestruction; override; class function NewInstance: TObject; override; property RefCount: Integer read FRefCount; end;
TInterfacedObject
implementiert das Interface IInterface
. Daher deklariert und implementiert TInterfacedObject
alle drei Methoden von IInterface
.
Klassen, die Interfaces implementieren, können auch als Basisklassen verwendet werden. (Im ersten der obigen Beispiele wird TMemoryManager
als direkter Nachkomme von TInterfacedObject
deklariert.) Da jedes Interface die Methoden von IInterface
erbt, muss eine Klasse, die Interfaces implementiert, auch die Methoden QueryInterface
, _AddRef
und _Release
implementieren. TInterfacedObject
in der Unit System
implementiert diese Methoden und eignet sich aus diesem Grund als Basis für weitere Klassen, die Interfaces implementieren.
Nach der Implementierung eines Interface wird jede seiner Methoden einer Methode der implementierenden Klasse zugeordnet, die denselben Ergebnistyp, dieselbe Aufrufkonvention und dieselbe Anzahl von Parametern hat (wobei entsprechende Parameter auch identische Typen haben müssen). Standardmäßig wird jede Methode des Interface der gleichnamigen Methode der implementierenden Klasse zugewiesen.
Methodenzuordnungsklausel
Das Standardverfahren der Methodenzuordnung in einer Klassendeklaration kann mithilfe von Methodenzuordnungsklauseln außer Kraft gesetzt werden. Wenn eine Klasse zwei oder mehr Interfaces mit identischen Methoden implementiert, können Sie die Namenskonflikte, die sich daraus ergeben, mithilfe von Methodenzuordnungsklauseln lösen.
Eine Methodenzuordnungsklausel sieht folgendermaßen aus:
procedure interface.interfaceMethod = implementingMethod;
oder:
function interface.interfaceMethod = implementingMethod;
Dabei ist implementingMethod eine in der Klasse deklarierte Methode oder eine Methode eines der Klassenvorfahren. Es kann sich dabei um eine an späterer Stelle deklarierte Methode handeln, nicht aber um eine als private deklarierte Methode einer Vorfahrklasse, die in einem anderen Modul deklariert ist.
Ein Beispiel:
type TMemoryManager = class(TInterfacedObject, IMalloc, IErrorInfo) function IMalloc.Alloc = Allocate; procedure IMalloc.Free = Deallocate; // ... end;
In dieser Klassendeklaration werden die Methoden Alloc
und Free
von IMalloc
den Methoden Allocate
und Deallocate
von TMemoryManager
zugeordnet.
Die in einer Vorfahrklasse festgelegten Zuordnungen können in Methodenzuordnungsklauseln nicht geändert werden.
Ändern von geerbten Implementierungen
Durch abgeleitete Klassen kann die Art und Weise, wie ein bestimmtes Interface implementiert ist, geändert werden, indem die Implementierungsmethode überschrieben wird. Dies setzt entweder eine virtuelle oder eine dynamische Implementierungsmethode voraus.
Eine Klasse kann auch ein vollständiges Interface erneut implementieren, das sie von einer Vorfahrklasse geerbt hat. Dies erfordert eine erneute Auflistung des Interface in der Deklaration der abgeleiteten Klasse. Zum Beispiel:
type IWindow = interface ['{00000115-0000-0000-C000-000000000146}'] procedure Draw; // ... end; TWindow = class(TInterfacedObject, IWindow) // TWindow implements IWindow pocedure Draw; // ... end; TFrameWindow = class(TWindow, IWindow) // TFrameWindow reimplements IWindow procedure Draw; // ... end;
Durch eine erneute Implementierung eines Interface wird die geerbte Implementierung desselben Interface verdeckt. Aus diesem Grund haben Methodenzuordnungsklauseln in der Vorgängerklasse keine Auswirkung auf das erneut implementierte Interface.
Implementieren von Interfaces durch Delegation
Die Direktive implements ermöglicht, die Implementierung eines Interface an eine Eigenschaft der implementierenden Klasse zu delegieren. Zum Beispiel:
property MyInterface: IMyInterface read FMyInterface implements IMyInterface;
Hier wird eine Eigenschaft namens MyInterface
deklariert, die das Interface IMyInterface
implementiert.
Die Direktive implements muss der letzte Bezeichner in der Eigenschaftsdeklaration sein und kann mehrere Interfaces enthalten, die durch Komma voneinander getrennt sind. Die Delegateigenschaft muss folgende Bedingungen erfüllen:
- Sie muss ein Klassen- oder Interface-Typ sein.
- Sie darf keine Array-Eigenschaft sein und keinen Index-Bezeichner verwenden.
- Sie muss über einen read-Bezeichner verfügen. Wenn es für die Eigenschaft eine read-Methode gibt, muss diese die standardmäßige Aufrufkonvention register verwenden. Außerdem darf die Methode nicht dynamisch (wohl aber virtuell) sein oder die Direktive message verwenden.
Die Klasse, die zur Implementierung des delegierten Interface verwendet wird, sollte von System.TAggregatedObject
abgeleitet sein.
Delegieren an eine Eigenschaft vom Typ Interface
Wenn die Delegateigenschaft ein Interface-Typ ist, muss dieses Interface bzw. das übergeordnete Interface in der Vorfahrenliste der Klasse enthalten sein, in der die Eigenschaft deklariert wird. Die Delegateigenschaft muss ein Objekt zurückgeben, dessen Klasse das mit der Direktive implements angegebene Interface vollständig implementiert. Dabei dürfen keine Methodenzuordnungsklauseln verwendet werden. Zum Beispiel:
type IMyInterface = interface procedure P1; procedure P2; end; TMyClass = class(TObject, IMyInterface) FMyInterface: IMyInterface; property MyInterface: IMyInterface read FMyInterface implements IMyInterface; end; var MyClass: TMyClass; MyInterface: IMyInterface; begin MyClass := TMyClass.Create; MyClass.FMyInterface := ...// some object whose class implements IMyInterface MyInterface := MyClass; MyInterface.P1; end;
Delegieren an eine Eigenschaft vom Typ Klasse
Wenn die Delegateigenschaft ein Klassentyp ist, werden diese Klasse und ihre Vorfahren nach Methoden durchsucht, welche das angegebene Interface implementieren. Danach werden bei Bedarf die umgebende Klasse und ihre Vorfahren durchsucht. Es ist daher möglich, einige Methoden in der durch die Eigenschaft bezeichneten Klasse zu deklarieren, andere dagegen in der Klasse, in der die Eigenschaft deklariert ist. Methodenzuordnungsklauseln können wie üblich verwendet werden, um Mehrdeutigkeiten aufzulösen oder um eine bestimmte Methode anzugeben. Ein Interface kann immer nur durch eine einzige Eigenschaft mit dem Typ einer Klasse implementiert werden. Zum Beispiel:
type IMyInterface = interface procedure P1; procedure P2; end; TMyImplClass = class procedure P1; procedure P2; end; TMyClass = class(TInterfacedObject, IMyInterface) FMyImplClass: TMyImplClass; property MyImplClass: TMyImplClass read FMyImplClass implements IMyInterface; procedure IMyInterface.P1 = MyP1; procedure MyP1; end; procedure TMyImplClass.P1; // ... procedure TMyImplClass.P2; // ... procedure TMyClass.MyP1; // ... var MyClass: TMyClass; MyInterface: IMyInterface; begin MyClass := TMyClass.Create; MyClass.FMyImplClass := TMyImplClass.Create; MyInterface := MyClass; MyInterface.P1; // calls TMyClass.MyP1; MyInterface.P2; // calls TImplClass.P2; end;