Delphi界面使用TValue进行投射

时间:2010-06-10 18:57:36

标签: delphi interface casting rtti

我最近对接口和D2010 RTTI进行了广泛的实验。我不知道在运行时实际的接口类型;虽然我可以使用字符串访问它的限定名称。

请考虑以下事项:

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)引用TChild实例作为IParent接口? (毕竟,硬编码的演员阵容很好。这甚至可能吗?)谢谢!

1 个答案:

答案 0 :(得分:2)

这看起来像是RTTI.pas中一个非常难看的延迟编码实例。在处理TValue中的界面强制转换的ConvIntf2Intf函数中,它明确地仅检查您是否正在转换为IInterface。任何其他接口将自动返回false。它可以轻松地提取GUID(如果您的接口有一个)并尝试QueryInterface调用,但它不会出于任何原因这样做。我会把这个报告给QC。