使用 TValue 进行 Delphi 接口(interface)转换

标签 delphi interface casting rtti

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

考虑以下因素:

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 进行所需的类型转换吗?换句话说:有没有办法让我知道 1) IChild 的限定名称作为字符串,以及 2) 对 TChild 实例作为 IParent 接口(interface)的引用来调用 IChild.ChildMethod ? (毕竟,硬编码的 Actor 工作正常。这可能吗?)谢谢!

最佳答案

这看起来像是 RTTI.pas 中一个非常丑陋的惰性编码实例。在负责 TValue 内接口(interface)转换的 ConvIntf2Intf 函数中,它仅显式检查是否要转换为 IInterface。任何其他接口(interface)都会自动返回 false。它可以轻松提取 GUID(如果您的界面有 GUID)并尝试 QueryInterface 调用,但无论出于何种原因它都不会这样做。我会将此事报告给 QC。

关于使用 TValue 进行 Delphi 接口(interface)转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3017532/

相关文章:

Delphi:ADOConnection、DBASE3 和字符集(错误?)

linux - 如何从 Linux 中使用 wsHttpBinding 访问 WCF SOAP 服务?

c++ - 动态转换智能指针

python - 将 float Series 中的所有元素转换为整数

C# - 使用反射的动态转换

delphi - 如何在Windows XP/Windows 2003上部署Delphi Soap Client(.EXE)?

delphi - 将匿名方法分配给接口(interface)变量或参数?

C# JavaScriptSerializer 序列化接口(interface)中不存在但存在于原始对象中的字段

windows - DUnit 比较两个文本文件并显示差异

TypeScript - 声明与导出 - 最佳实践?