IX. RTTI et génériques▲
En dernier chapitre de ce tutoriel, voici quelques informations sur ce que deviennent les RTTI avec les génériques. Si vous ne jouez jamais avec les RTTI, vous pouvez sauter entièrement ce chapitre. Ce n'est pas du tout une introduction aux RTTI.
IX-A. Les changements sur la pseudoroutine TypeInfo▲
Les RTTI, ça commence toujours par la pseudoroutine TypeInfo. Vous savez peut-être qu'on ne peut pas appeler cette pseudoroutine sur n'importe quel type ; exemple : les types pointeur. Et que, de ce fait, elle ne renvoie jamais nil.
Alors, peut-on appeler TypeInfo sur un type générique T ? La question est pertinente : T pourrait bien être un type pointeur (invalide pour TypeInfo), mais également un type entier par exemple (valide pour TypeInfo).
La réponse est oui, on peut appeler TypeInfo sur un type générique. Mais que se passe-t-il alors si T se trouve être un type qui n'a pas de RTTI ? Eh bien, dans ce cas, et dans ce cas seulement, TypeInfo renvoie nil.
Pour illustrer la chose, voici une petite méthode de classe qui affiche le nom et la sorte d'un type, mais via des génériques :
type
TRTTI = class
(TObject)
public
class
procedure
PrintType<T>; static
;
end
;
class
procedure
TRTTI.PrintType<T>;
var
Info: PTypeInfo;
begin
Info := TypeInfo(T); // attention ! Info peut valoir nil ici
if
Info = nil
then
WriteLn('Ce type ne possède pas de RTTI'
)
else
WriteLn(Info.Name, #9
, GetEnumName(TypeInfo(TTypeKind), Byte
(Info.Kind)));
end
;
On l'utilise de manière un peu particulière, dans le sens où le véritable paramètre de la routine PrintType est transmis en tant que type paramétré.
begin
TRTTI.PrintType<Integer
>;
TRTTI.PrintType<TObject>;
TRTTI.PrintType<Pointer
>;
end
;
Ce qui donne :
Integer tkInteger
TObject tkClass
Ce type ne possède pas de RTTI
IX-A-1. Une fonction TypeInfo plus générale▲
Il m'est déjà arrivé de regretter que TypeInfo ne puisse pas être appelée sur n'importe quel type, quitte à recevoir nil ; peut-être que vous aussi. Voici donc une petite méthode de remplacement qui fait ça, à base de génériques.
type
TRTTI = class
(TObject)
public
class
procedure
PrintType<T>; static
;
class
function
TypeInfo<T>: PTypeInfo; static
;
end
;
class
function
TRTTI.TypeInfo<T>: PTypeInfo;
begin
Result := System.TypeInfo(T);
end
;
Que vous pouvez utiliser comme ceci :
Info := TRTTI.TypeInfo<Pointer
>; // Info = nil
// au lieu de :
Info := TypeInfo(Pointer
); // erreur ici, car Pointer n'a pas de RTTI
IX-B. Les types génériques ont-ils des RTTI ?▲
Il y a deux questions à se poser : est-ce que les types génériques non instanciés (avec donc un paramètre T non défini) ont des RTTI ? Et est-ce que les types génériques instanciés (donc où T a été remplacé par un type réel, comme Integer) ont des RTTI ?
Le plus facile pour le savoir est de l'essayer ;-) Vous pourrez observer, en testant, que seuls les types génériques instanciés ont des RTTI. En fait, c'est assez logique, dans la mesure où les types génériques non instanciés ne sont pas réellement des types, mais des modèles de type, et n'existent tout simplement plus du tout une fois passée la compilation.
On peut se demander quel nom on va trouver pour de tels types. Alors voici :
begin
TRTTI.PrintType<TComparison<Integer
>>;
TRTTI.PrintType<TTreeNode<Integer
>>;
end
;
Ce qui donne :
TComparison<System.Integer> tkInterface
TTreeNode<System.Integer> tkClass
Ce qui montre que le nom comprend, entre chevrons, le nom complètement qualifié du type réel remplaçant le type générique.
Vous pouvez aussi remarquer qu'on obtient bien tkInterface pour le type référence de routine TComparison<T>, ce qui prouve bien que c'est une interface.
Voilà, ce sont les seuls changements apportés aux RTTI avec l'avènement des génériques. Je ne parle bien sûr pas d'autres changements apportés dans cette version, mais qui concernent les chaînes Unicode.