Références d'interfaces (Delphi)

De RAD Studio
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'interface et indiquent les rubriques associées.

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

Les variables des références d'interface 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 dérivée sauf si la classe (ou une classe qui en dérive 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ée 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 assignation légale. D est déclarée comme variable de type IDescendant. Quand D désigne 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'interface sont 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 à quel moment une expression de type interface désigne un objet, il faut la transmettre à la fonction standard Assigned.

Compatibilité des assignations d'interfaces

Les variables d'un type classe donné sont compatibles pour l'assignation avec tous les types interface implémentés par la classe. Les variables d'un type interface sont compatibles pour l'assignation 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 transtypé en IInterface. Un variant dont le code de type est varEmpty ou varDispatch peut être transtypé en 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 pafin d'effectuer des transtypages d'interface 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 :

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;

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.

L'interrogation d'une interface renvoie nil si object vaut nil. Sinon, elle transmet le GUID de 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 Delphi. 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 pas suffisant.

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, tandis que le dernier renvoie nil. L'exemple ci-dessus 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 Delphi seulement lors de l'utilisation du transtypage non sécurisé ou des opérateurs as et is.

Voir aussi