Interface-Referenzen (Delphi)

Aus RAD Studio
Wechseln zu: Navigation, Suche

Nach oben zu Objekt-Interfaces - Index


Eine mit einem Interface-Typ deklarierte Variable kann Instanzen jeder Klasse referenzieren, die von dem Interface implementiert wird. In den folgenden Abschnitten werden Interface-Referenzen beschrieben und verwandte Themen angegeben.

Interface-Referenzen implementieren

Mithilfe von Interface-Referenzen können Interface-Methoden auch aufgerufen werden, wenn zur Compilierzeit noch nicht bekannt ist, wo das Interface implementiert wird. Es gelten jedoch folgende Einschränkungen:

  • Mit Ausdrücken vom Typ eines Interface kann nur auf Methoden und Eigenschaften zugegriffen werden, die in dem Interface deklariert sind. Ein Zugriff auf andere Elemente der implementierenden Klasse ist nicht möglich.
  • Ein Ausdruck vom Typ eines Interface kann nur dann ein Objekt referenzieren, dessen Klasse ein abgeleitetes Interface implementiert, wenn die Klasse (bzw. eine übergeordnete Klasse) das abgeleitete Interface explizit implementiert.

Beispiel:

 type
   IAncestor = interface
   end;
   IDescendant = interface(IAncestor)
     procedure P1;
   end;
   TSomething = class(TInterfacedObject, IDescendant)
     procedure P1;
     procedure P2;
   end;
      // ...
 var
   D: IDescendant;
   A: IAncestor;
 begin
   D := TSomething.Create;  // works!
   A := TSomething.Create;  // error
   D.P1;  // works!
   D.P2;  // error
 end;

In diesem Beispiel ist A als Variable des Typs IAncestor deklariert. Da IAncestor nicht in den von TSomething implementierten Interfaces enthalten ist, kann A keine Instanz von TSomething zugewiesen werden. Wenn Sie aber die Deklaration von TSomething folgendermaßen ändern:

 TSomething = class(TInterfacedObject, IAncestor, IDescendant)
  // ...

wird der erste Fehler zu einer gültigen Zuweisung. D ist als Variable des Typs IDescendant deklariert. Solange D eine Instanz von TSomething referenziert, kann damit nicht auf die Methode P2 von TSomething zugegriffen werden (P2 ist keine Methode von IDescendant). Wenn Sie aber die Deklaration von D folgendermaßen ändern:

 D: TSomething;

wird der zweite Fehler zu einem gültigen Methodenaufruf.

Interface-Referenzen werden auf der Win32-Plattform über einen Referenzzähler verwaltet, der auf den von System.IInterface geerbten Methoden _AddRef und _Release basiert. Bei Verwendung der Standardimplementierung der Referenzzählung braucht ein Objekt, das ausschließlich durch Interfaces referenziert wird, nicht manuell freigegeben zu werden. Seine Freigabe erfolgt automatisch, wenn der Referenzzähler den Wert Null erreicht. Einige Klassen implementieren Interfaces, um diese Standard-Lebensdauerverwaltung zu umgehen, und einige Hybridobjekte verwenden die Referenzzählung nur, wenn das Objekt keinen Eigentümer hat.

Für globale Variablen vom Typ eines Interface ist nur der Initialisierungswert nil zulässig.

Um festzustellen, ob ein Ausdruck vom Typ eines Interface ein Objekt referenziert, übergeben Sie ihn an die Standardfunktion Assigned.

Zuweisungskompatibilität von Interfaces

Variablen eines bestimmten Klassentyps sind zu jedem Interface-Typ zuweisungskompatibel, der von der Klasse implementiert wird. Variablen eines Interface-Typs sind zu jedem Interface-Typ kompatibel, von dem er abgeleitet ist. Jeder Variable vom Typ eines Interface kann der Wert nil zugewiesen werden.

Ein Ausdruck vom Typ eines Interface kann einer Variante zugewiesen werden. Wenn ein Interface vom Typ IDispatch oder ein Nachkomme davon ist, hat die resultierende Variante den Typcode varDispatch. Andernfalls hat die Variante den Typcode varUnknown.

Eine Variante mit dem Typcode varEmpty, varUnknown oder varDispatch kann einer IInterface-Variable zugewiesen werden. Varianten mit dem Typcode varEmpty und varDispatch können einer IDispatch-Variable zugewiesen werden.

Interface-Umwandlungen

Ein Ausdruck vom Typ eines Interface kann in den Typ Variant konvertiert werden. Wenn ein Interface vom Typ IDispatch oder ein Nachkomme davon ist, hat die resultierende Variante den Typcode varDispatch. Andernfalls hat die Variante den Typcode varUnknown.

Eine Variante mit dem Typcode varEmpty, varUnknown oder varDispatch kann in IInterface umgewandelt werden. Varianten mit dem Typcode varEmpty oder varDispatch können in den Typ IDispatch umgewandelt werden.

Interface-Abfragen

Mithilfe des Operators as können Interface-Umwandlungen durchgeführt werden. Dieser Vorgang wird als Interface-Abfrage bezeichnet. Eine Interface-Abfrage generiert auf der Basis des (zur Laufzeit vorliegenden) Objekttyps aus einer Objektreferenz oder einer anderen Interface-Referenz einen Ausdruck vom Typ eines Interface. Die Syntax für eine Interface-Abfrage lautet:

Objekt as Interface

Objekt ist entweder ein Ausdruck vom Typ eines Interface oder einer Variante, oder er bezeichnet eine Instanz einer Klasse, die ein Interface implementiert. Interface kann jedes mit einer GUID deklarierte Interface sein.

Wenn Objekt den Wert nil hat, liefert die Interface-Abfrage als Ergebnis nil. Andernfalls wird die GUID des Interface an die Methode QueryInterface von Objekt übergeben. Wenn QueryInterface einen Wert ungleich Null zurückgibt, wird eine Exception ausgelöst. Ist der Rückgabewert dagegen Null (d.h. das Interface ist in der Klasse von Objekt implementiert), ergibt die Interface-Abfrage eine Referenz auf Objekt.

Umwandeln von Interface-Referenzen in Objekte

Mit dem Operator as kann auch eine Typumwandlung einer Interface-Referenz zurück in das Objekt durchgeführt werden, aus dem sie ermittelt wurde. Diese Typumwandlung gilt für Interfaces, die aus Delphi-Objekten ermittelt wurden. Zum Beispiel:

 var
   LIntfRef: IInterface;
   LObj: TInterfacedObject;
 begin
   { Objekt mit Interface erstellen und Interface daraus extrahieren. }
   LIntfRef := TInterfacedObject.Create();
 
   { Interface zurück in des Ursprungsobjekt konvertieren. }
   LObj := LIntfRef as TInterfacedObject;
 end;

Das obige Beispiel demonstriert, wie das Ursprungsobjekt ermittelt wird, aus dem die Interface-Referenz abgerufen wurde. Diese Technik eignet sich besonders, wenn das Verfügen über eine Interface-Referenz nicht ausreicht.

Der Operator as löst eine Exception aus, wenn das Interface nicht aus der angegebenen Klasse extrahiert wurde:

 var
   LIntfRef: IInterface;
   LObj: TInterfacedObject;
 begin
   { Objekt mit Interface erstellen und Interface daraus extrahieren. }
   LIntfRef := TInterfacedObject.Create();
 
   try
     { Interface in TComponent umwandeln. }
     LObj := LIntfRef as TComponent;
   except
     WriteLn('LIntfRef was not referencing a TComponent instance');
   end;  
 end;

Sie können auch eine normale (unsichere) Typumwandlung einer Interface-Referenz in ein Objekt vornehmen. Diese Methode löst keine Exceptions aus. Der Unterschied zwischen der unsicheren Objekt-in-Objekt-Typumwandlung in der unsicheren Interface-in-Objekt-Typumwandlung ist folgender: die erste Typumwandlung gibt bei inkompatiblen Typen einen gültigen Zeiger zurück, die zweite gibt nil zurück. Das folgende Beispiel zeigt die Verwendung der unsicheren Typumwandlung:

 var
   LIntfRef: IInterface;
   LObj: TInterfacedObject;
 begin
   { Objekt mit Interface erstellen und Interface daraus extrahieren. }
   LIntfRef := TInterfacedObject.Create();
 
   { Interface in TComponent umwandeln. }
   LObj := TComponent(LIntfRef);
 
   if LObj = nil then
     WriteLn('LIntfRef was not referencing a TComponent instance');
 
   { Interface in TObject umwandeln. }
   LObj := TObject(LIntfRef);
 
   if LObj <> nil then
     WriteLn('LIntfRef was referencing a TObject (or descendant).');
 end;

Um potentielle nil-Referenzen zu vermeiden, sollten Sie mit dem Operator is überprüfen, ob die Interface-Referenz vorn der angegebenen Klasse extrahiert wurde:

 if Intf is TCustomObject then ...

Hinweis: Verwenden Sie mit der unsicheren Typumwandlung oder den Operatoren as und is ausschließlich Delphi Objekte.

Siehe auch