2010-10-01 29 views
6

Je dois obtenir le nom de l'unité (espace de nom) de TRttiType. Jusqu'ici, j'ai essayé ce qui suit:Obtenir le nom de l'unité qui appartient à un type (TRttiType)

1) en utilisant le PTypeData.UnitName, cette solution fonctionne, mais seulement lorsque TTypeKind est tkClass.

procedure ListAllUnits; 
var 
    ctx : TRttiContext; 
    lType: TRttiType; 
    Units: TStrings; 
begin 
    Units:=TStringList.Create; 
    try 
    ctx := TRttiContext.Create; 
    for lType in ctx.GetTypes do 
    if lType.IsInstance then //only works for classes 
     if Units.IndexOf(UTF8ToString(GetTypeData(lType.Handle).UnitName))<0 then 
     Units.Add(UTF8ToString(GetTypeData(lType.Handle).UnitName)); 
    Writeln(Units.Text); 
    finally 
    Units.Free; 
    end; 
end; 

2) Parsing la propriété QualifiedName, cette solution fonctionne bien jusqu'à maintenant, mais je ne suis pas très heureux avec elle.

procedure ListAllUnits2; 

    function GetUnitName(lType: TRttiType): string; 
    begin 
    Result := StringReplace(lType.QualifiedName, '.' + lType.Name, '',[rfReplaceAll]) 
    end; 

var 
    ctx: TRttiContext; 
    lType: TRttiType; 
    Units: TStrings; 
begin 
    Units := TStringList.Create; 
    try 
    ctx := TRttiContext.Create; 
    for lType in ctx.GetTypes do 
     if Units.IndexOf(GetUnitName(lType)) < 0 then 
     Units.Add(GetUnitName(lType)); 
    Writeln(Units.Text); 
    finally 
    Units.Free; 
    end; 
end; 

la question est, existe une autre façon fiable pour obtenir le nom de l'unité de tout TRttiType?

Répondre

5

Il ne semble pas y en avoir. Le RTTI provient de la structure TTypeData, qui n'a qu'un champ UnitName explicitement déclaré pour des types spécifiques. (Ceci est antérieur à D2010 et étendu RTTI.) Votre # 2 ressemble à la meilleure façon de l'obtenir, et est probablement comment un hypothétique TRTTIObject.UnitName le calculerait s'ils devaient en mettre un.

5

L'information est là mais L'analyse du nom qualifié est actuellement le meilleur moyen d'y accéder.

Si vous voulez le faire à la dure, vous pouvez par:

Dans l'unité de System.pas vous avez une LibModuleList: PLibModule = nil; variable qui contient la liste des modules chargés. C'est le pointeur vers les informations RTTI brutes qui peuvent être utilisées sans RTTI.pas. Vous pouvez parcourir toutes les informations brutes pour déterminer le nom de l'unité.

Les valeurs clés du TLibModule sont:

PLibModule = ^TLibModule; 
    TLibModule = record 
    Next: PLibModule; { Linked List of Loaded Modules) 
    Instance: LongWord; 
    ... 
    TypeInfo: PPackageTypeInfo; { List of contained Package Information } 
    ... 
    end; 

Utilisation du TypeInfo: PPackageTypeInfo; vous avez accès à

PPackageTypeInfo = ^TPackageTypeInfo; 
    TPackageTypeInfo = record 
    TypeCount: Integer; 
    TypeTable: PTypeTable; 
    UnitCount: Integer; 
    UnitNames: PShortString; { concatenation of Pascal strings, 
           one for each unit } 
    end; 

Ensuite, il y a typetable qui contient les informations pour se rendre à PTypeInfo.
séquence.

TTypeTable = array[0..MaxInt div SizeOf(Pointer) - 1] of Pointer; 
    PTypeTable = ^TTypeTable; 

Un exemple de la façon dont tout cela fonctionne se trouvent dans Rtti.pas TPackage.MakeTypeLookupTable est la méthode clé. Cette méthode montre également que QualifiedName contiendra toujours le UnitName. En tant que tel, votre méthode originale d'analyse QualfiedName peut être dépendante.