2010-06-10 100 views
1

我最近對界面和D2010 RTTI進行了廣泛的實驗。我在運行時不知道接口的實際類型;儘管我可以使用字符串訪問它的限定名稱。使用TValue進行Delphi接口投射

考慮以下幾點:

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. 

mynamespace.pas內容如下:

{$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; 

爲了完整起見,實施竟把

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; 

原因{$define WORKAROUND}可能發現in this post

問題:有什麼辦法讓我使用RTTI進行想要的類型轉換嗎?換句話說:有沒有辦法讓我調用IChild.ChildMethod來知道1)IChild的限定名作爲字符串,2)作爲IParent接口的TChild實例的引用? (畢竟,硬編碼工作正常,甚至有可能嗎?)謝謝!

回答

2

這看起來像是RTTI.pas中惰性編碼的一個非常不好的實例。在負責處理TValue中的接口強制轉換的ConvIntf2Intf函數中,它明確地僅檢查是否投射到IInterface。任何其他接口將自動返回false。它可以很容易地提取GUID(如果你的接口有一個)並嘗試一個QueryInterface調用,但不會出於任何原因這樣做。我會把這個報告給QC。

+0

男人,這很有道理!我真的不明白爲什麼這不起作用。再次拯救! :)(+1)我會在早上向QC彙報。 – conciliator 2010-06-10 20:07:26

+0

早上比預期更快......現在在QC#85339下報告。 – conciliator 2010-06-10 20:22:42