2010-06-10 20 views
1

J'ai récemment expérimenté intensivement avec les interfaces et le RTTI D2010. Je ne sais pas au moment de l'exécution le type réel de l'interface; bien que j'aurai accès à son nom qualifié en utilisant une chaîne.Diffusion de l'interface Delphi à l'aide de TValue

Considérez ce qui suit:

program rtti_sb_1; 
{$APPTYPE CONSOLE} 
uses 
    SysUtils, Rtti, TypInfo, mynamespace in 'mynamespace.pas'; 
var 
    ctx:     TRttiContext; 
    InterfaceType:  TRttiType; 
    Method:    TRttiMethod; 
    ActualParentInstance: IParent; 
    ChildInterfaceValue: TValue; 
    ParentInterfaceValue: TValue; 
begin 
    ctx := TRttiContext.Create; 
    // Instantiation 
    ActualParentInstance := TChild.Create as IParent; 
    {$define WORKAROUND} 
    {$ifdef WORKAROUND} 
    InterfaceType := ctx.GetType(TypeInfo(IParent)); 
    InterfaceType := ctx.GetType(TypeInfo(IChild)); 
    {$endif} 
    // Fetch interface type 
    InterfaceType := ctx.FindType('mynamespace.IParent'); 
    // This cast is OK and ChildMethod is executed 
    (ActualParentInstance as IChild).ChildMethod(100); 
    // Create a TValue holding the interface 
    TValue.Make(@ActualParentInstance, InterfaceType.Handle, ParentInterfaceValue); 
    InterfaceType := ctx.FindType('mynamespace.IChild'); 
    // This cast doesn't work 
    if ParentInterfaceValue.TryCast(InterfaceType.Handle, ChildInterfaceValue) then begin 
    Method := InterfaceType.GetMethod('ChildMethod'); 
    if (Method <> nil) then begin 
     Method.Invoke(ChildInterfaceValue, [100]); 
    end; 
    end; 
    ReadLn; 
end. 

Le contenu de mynamespace.pas est la suivante:

{$M+} 
IParent = interface 
    ['{2375F59E-D432-4D7D-8D62-768F4225FFD1}'] 
    procedure ParentMethod(const Id: integer); 
end; 
{$M-} 
IChild = interface(IParent) 
    ['{6F89487E-5BB7-42FC-A760-38DA2329E0C5}'] 
    procedure ChildMethod(const Id: integer); 
end; 
TParent = class(TInterfacedObject, IParent) 
public 
    procedure ParentMethod(const Id: integer); 
end; 
TChild = class(TParent, IChild) 
public 
    procedure ChildMethod(const Id: integer); 
end; 

Pour être complet, la mise en œuvre se déroule comme

procedure TParent.ParentMethod(const Id: integer); 
begin 
    WriteLn('ParentMethod executed. Id is ' + IntToStr(Id)); 
end; 
procedure TChild.ChildMethod(const Id: integer); 
begin 
    WriteLn('ChildMethod executed. Id is ' + IntToStr(Id)); 
end; 

La raison {$define WORKAROUND} peut être trouvé in this post. Question: est-ce qu'il y a un moyen pour moi de faire le cast de type désiré en utilisant RTTI? En d'autres termes: existe-t-il un moyen pour moi d'invoquer IChild.ChildMethod de savoir 1) le nom qualifié de IChild en tant que chaîne, et 2) une référence à l'instance TChild en tant qu'interface IParent? (Après tout, la distribution codée en dur fonctionne bien, est-ce possible?) Merci!

Répondre

2

Cela ressemble à une instance assez laide du codage paresseux dans RTTI.pas. Dans la fonction ConvIntf2Intf qui prend en charge les conversions d'interface dans TValue, elle vérifie uniquement si vous diffusez IInterface. Toute autre interface retournera false automatiquement. Il pourrait facilement extraire le GUID (si votre interface en a un) et tenter un appel QueryInterface, mais il ne le fait pas pour une raison quelconque. Je signalerais celui-ci au QC.

+0

L'homme, ça a tellement de sens! Je ne pouvais vraiment pas comprendre pourquoi cela ne fonctionnait pas. A la rescousse encore une fois! :) (+1) Je vais le signaler à QC dans la matinée. – conciliator

+0

Le matin est venu plus vite que prévu ... Il est maintenant signalé sous QC# 85339. – conciliator