Interface-Referenzen (Object Pascal)

Aus Appmethod Topics
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. Die folgenden Abschnitte enthalten eine Beschreibung von Interface-Referenzen und Verweise auf verwandte Themen.

Implementieren von Interface-Referenzen

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:

  • Über Ausdrücke mit dem Typ "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 mit dem Typ "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.

Zum 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 mit dem Typ 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 mit dem Typ 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 "Interface" ist nur der Initialisierungswert nil zulässig.

Um festzustellen, ob ein Ausdruck mit dem Typ "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 mit einem Interface-Typ sind mit jedem Vorfahr-Interface-Typ kompatibel. Jeder Variable mit dem Typ "Interface" kann der Wert nil zugewiesen werden.

Ein Ausdruck mit dem Typ "Interface" kann einer Variante zugewiesen werden. Wenn ein Interface den Typ IDispatch hat 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 mit dem Typ "Interface" kann in den Typ "Variant" konvertiert werden. Wenn ein Interface den Typ IDispatch hat 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 überprüfte 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 mit dem Typ "Interface". Die Syntax für eine Interface-Abfrage lautet:

object as interface

object ist entweder ein Ausdruck mit dem Typ "Interface" oder "Variant", oder es bezeichnet eine Instanz einer Klasse, die ein Interface implementiert. Interface kann jedes mit einer GUID deklarierte Interface sein.

Wenn object den Wert nil hat, liefert die Interface-Abfrage als Ergebnis nil. Andernfalls wird die GUID des Interface an die Methode QueryInterface von object ü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 object implementiert), ergibt die Interface-Abfrage eine Referenz auf object.

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 Object Pascal-Objekten ermittelt wurden. Zum Beispiel:

 var
    LIntfRef: IInterface;
    LObj: TInterfacedObject;
  begin
    { Create an interfaced object and extract an interface from it. }
    LIntfRef := TInterfacedObject.Create();
  
    { Cast the interface back to the original object. }
    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
    { Create an interfaced object and extract an interface from it. }
    LIntfRef := TInterfacedObject.Create();
  
    try
      { Cast the interface to a TComponent. }
      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-zu-Objekt-Typumwandlung und der unsicheren Interface-zu-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
    { Create an interfaced object and extract an interface from it. }
    LIntfRef := TInterfacedObject.Create();
  
    { Cast the interface to a TComponent. }
    LObj := TComponent(LIntfRef);
  
    if LObj = nil then
      Writeln('LIntfRef was not referencing a TComponent instance');
  
    { Cast the interface to a TObject. }
    LObj := TObject(LIntfRef);
  
    if LObj <> nil then
      Writeln('LIntfRef was referencing a TObject (or descendant).');
  end;

Um potenzielle nil-Referenzen zu vermeiden, sollten Sie mit dem Operator is überprüfen, ob die Interface-Referenz aus 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 Object Pascal-Objekte.

Siehe auch