Extraction des attributs à l'exécution

De RAD Studio
Aller à : navigation, rechercher

Remonter à Attributs (RTTI)

Fournit des informations sur les aspects à l'exécution des attributs ; comment les extraire et comment prendre une décision personnalisée selon leur valeur informationnelle.

Instanciation d'attribut

L'annotation (telle que traitée dans Annotation des types et des membres de types) est une méthode simple d'attachement d'un attribut à un type ou un membre. Les informations incluses dans le binaire compilé incluent seulement la classe de l'attribut, le pointeur au constructeur sélectionné et la liste des constantes qui sont passées au constructeur de l'attribut au moment de l'instanciation.

L'instanciation réelle des attributs se produit quand le code consommateur les interroge dans un type ou un membre de type donné. Cela signifie que les instances des classes d'attributs ne sont pas créées automatiquement, mais plutôt quand le programme les recherche explicitement. Il n'existe pas d'ordre garanti dans lequel les attributs sont instanciés, et la façon dont les nombreuses instances sont créées n'est pas connue. Un programme ne doit pas dépendre de telles conséquences.

Considérez la déclaration d'attribut suivante :

type
  TSpecialAttribute = class(TCustomAttribute)
  public
    FValue: String;

    constructor Create(const AValue: String);
  end;

constructor TSpecialAttribute.Create(const AValue: String);
begin
  FValue := AValue;
end;

TSpecialAttribute est alors utilisé en tant qu'annotation dans l'exemple suivant :

type
  [TSpecialAttribute('Hello World!')]
  TSomeType = record
  ...
  end;

Pour extraire l'attribut du type TSomeType, le code utilisateur doit employer la fonctionnalité exposée par l'unité System.Rtti. L'exemple suivant illustre le code d'extraction :

var
  LContext: TRttiContext;
  LType: TRttiType;
  LAttr: TCustomAttribute;
begin
  { Create a new Rtti context }
  LContext := TRttiContext.Create
  
  { Extract type information for TSomeType type }
  LType := LContext.GetType(TypeInfo(TSomeType));

  { Search for the custom attribute and do some custom processing }
  for LAttr in LType.GetAttributes() do
    if LAttr is TSpecialAttribute then
      Writeln(TSpecialAttribute(LAttr).FValue);
 
  { Destroy the context }
  LContext.Free;
end;

Comme vous l'avez vu dans l'exemple ci-dessus, l'utilisateur doit écrire spécifiquement du code pour interroger les attributs annotés sur un type. Les instances d'attributs réelles sont créées dans la méthode TRttiType.GetAttributes. Notez que l'exemple ne détruit pas les instances; TRttiContext libère toutes les ressources par la suite.

Exceptions

Puisque l'instanciation réelle des attributs est effectuée dans le code utilisateur, vous devez être conscient des exceptions possibles qui peuvent survenir dans les constructeurs des attributs. La recommandation générale est d'utiliser une clause try .. except encadrant le code qui interroge les attributs.

Pour illustrer le problème, le constructeur d'attribut de l'exemple d'origine est changé comme suit :

constructor TSpecialAttribute.Create(const AValue: String);
begin
  if AValue =  then
    raise EArgumentException.Create('Expected a non-null string');

  FValue := AValue;
end;

et l'annotation pour TSomeType est changée afin de passer une chaîne vide au constructeur d'attribut :

type
  [TSpecialAttribute('')]
  TSomeType = record
  ...
  end;

Dans ce cas, le code qui interroge les attributs du type TSomeType échouera avec une exception EArgumentException qui est déclenchée par l'attribut d'instanciation. La recommandation générale est de changer le code d'interrogation afin d'utiliser la clause try .. except :

  { Search for the custom attribute and do some custom processing }
  try
    for LAttr in LType.GetAttributes() do
      if LAttr is TSpecialAttribute then
        Writeln(TSpecialAttribute(LAttr).FValue);
  except
    { ... Do something here ... }
  end;

Voir aussi