Références d'interfaces (Object Pascal)

De Appmethod Topics
Aller à : navigation, rechercher

Remonter à Interfaces d'objets - Index


Si vous déclarez une variable de type interface, la variable peut référencer des instances de toute classe qui implémentent l'interface. Ces rubriques décrivent les références d'interfaces et indiquent les rubriques associées.

Implémentation des références d'interfaces

Les variables des références d'interfaces vous permettent d'appeler les méthodes de l'interface sans savoir, au moment de la compilation, où l'interface est implémentée. Leur utilisation est sujette aux contraintes suivantes :

  • Une expression de type interface ne vous donne accès qu'aux méthodes et propriétés déclarées dans l'interface et pas aux autres membres de la classe d'implémentation.
  • Une expression de type interface ne peut référencer un objet dont la classe implémente une interface descendante, sauf si la classe (ou une classe héritée) implémente aussi explicitement l'interface ancêtre.

Par exemple :

 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;

Dans cet exemple, A est déclaré comme variable de type IAncestor. Comme TSomething ne liste pas IAncestor dans les interfaces qu'elle implémente, une instance de TSomething ne peut pas être assignée à A. Mais, si vous avez changé la déclaration de TSomething en :

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

la première erreur devient une affectation valide. D est déclaré comme variable de type IDescendant. Quand D référence une instance de TSomething, il n'est pas possible de l'utiliser pour accéder à la méthode P2 de TSomething, car P2 n'est pas une méthode de IDescendant. Mais, si vous avez changé la déclaration de D en :

 D: TSomething;

la deuxième erreur devient un appel de méthode valide.

Sur la plate-forme Win32, les références d'interfaces sont typiquement gérées par un système de comptage de références qui dépend des méthodes _AddRef et _Release héritées de System/IInterface. Avec l'implémentation par défaut du compteur de références, quand un objet n'est référencé que par l'intermédiaire d'interfaces, il n'est pas nécessaire de le détruire manuellement : l'objet est détruit automatiquement quand sa dernière référence sort de portée. Certaines classes implémentent les interfaces en contournant cette gestion par défaut, et certains objets hybrides utilisent le décompte de références uniquement quand l'objet n'a pas de propriétaire.

La seule initialisation possible pour les variables globales de type interface est nil.

Pour déterminer si une expression de type interface référence un objet, il faut la transmettre à la fonction standard Assigned.

Compatibilité des affectations d'interfaces

Les variables d'un type classe donné sont compatibles pour l'affectation avec tous les types interface implémentés par la classe. Les variables d'un type interface sont compatibles pour l'affectation avec tous ses types interface ancêtre. Il est possible d'assigner la valeur nil à toute variable de type interface.

Il est possible d'assigner une expression de type interface à un variant. Si l'interface est de type IDispatch ou d'un type descendant, le variant reçoit le code de type varDispatch. Sinon, le variant reçoit le code de type varUnknown.

Un variant dont le code de type est varEmpty, varUnknown ou varDispatch peut être assigné à une variable IInterface. Un variant dont le type de code est varEmpty ou varDispatch peut être assigné à une variable IDispatch.

Transtypage d'interfaces

Une expression de type interface peut être transtypée en Variant. Si l'interface est de type IDispatch ou d'un type descendant, le variant résultant a le code de type varDispatch. Sinon, le variant résultant a le code de type varUnknown.

Un variant dont le code de type est varEmpty, varUnknown ou varDispatch peut être transtypé en IInterface. Un variant dont le code de type est varEmpty ou varDispatch peut être transtypé en IDispatch.

Interrogation d'interface

Vous pouvez utiliser l'opérateur as afin d'effectuer des transtypages d'interfaces avec vérification. Ce mécanisme s'appelle l'interrogation d'interface ; il produit une expression de type interface depuis une référence d'objet ou une autre référence d'interface à partir du type réel (à l'exécution) d'objet. Une interrogation d'interface a la forme suivante :

object as interface

object est une expression de type interface, de type variant ou désignant une instance d'une classe qui implémente une interface, et où interface est une interface déclarée avec un GUID.

Une interrogation d'interface renvoie nil si object vaut nil. Sinon, elle transmet le GUID de l'interface à la méthode QueryInterface de object et déclenche une exception sauf si QueryInterface renvoie zéro. Si QueryInterface renvoie zéro (ce qui indique que la classe de object implémente l'interface), l'interrogation d'interface renvoie une référence à object.

Transtypage des références d'interfaces en objets

L'opérateur as peut aussi être utilisé pour transtyper une référence d'interface sur l'objet à partir duquel elle a été obtenue. Ce transtypage fonctionne seulement pour les interfaces obtenues à partir d'objets Object Pascal. Par exemple :

 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;

L'exemple ci-dessus montre comment obtenir l'objet original à partir duquel la référence d'interface a été obtenue. Cette technique est utile quand la possession d'une référence d'interface est simplement insuffisante.

L'opérateur as déclenche une exception si l'interface n'a pas été extraite de la classe donnée :

 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;

Vous pouvez aussi effectuer un transtypage normal (non sécurisé) d'une référence d'interface en un objet. Comme dans le transtypage non sécurisé d'un objet, cette méthode ne déclenche pas d'exceptions. La différence entre le transtypage objet-en-objet non sécurisé et le transtypage interface-en-objet non sécurisé est que le premier renvoie un pointeur valide en cas de types incompatibles, alors que le dernier renvoie nil. Cet exemple décrit l'usage d'un transtypage non sécurisé :

 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;

Pour éviter des références nil potentielles, utilisez l'opérateur is pour vérifier si la référence d'interface a été extraite d'une classe donnée :

 if Intf is TCustomObject then ...

Remarque : Assurez-vous d'utiliser des objets uniquement Object Pascal lors de l'utilisation du transtypage non sécurisé ou des opérateurs as et is.

Voir aussi