Interfaces implementieren

Aus Appmethod Topics
Wechseln zu: Navigation, Suche

Nach oben zu Objekt-Interfaces - Index

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 Klassendeklaration 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).

Die Deklaration von System.TInterfacedObject sieht beispielsweise wie folgt aus:

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 vererbten 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 es, 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 Kommas voneinander getrennt sind. Die "beauftragte" Eigenschaft 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 "beauftragte" Eigenschaft 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 beauftragte Eigenschaft 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 "beauftragte" Eigenschaft 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;

Siehe auch