7

假设我有一个示例类助手

TSampleClassHelper = class helper for TSampleClass
public
  procedure SomeHelper;
end;

我执行以下操作:

var
  obj :TSampleClass;
begin
  obj:=TSampleClass.Create;
  obj.SomeHelper;
end;

这按预期工作。

但是我怎样才能使用 RTTI 来调用辅助方法呢?以下似乎不起作用,GetMethod返回零。

var
  obj :TSampleClass;
  ctx :TRTTIContext;
  rtype :TRTTIType;
  rmethod :TRTTIMethod;
begin
  obj:=TSampleClass.Create;
  rtype:=ctx.GetType(obj.ClassType);
  rmethod:=rtype.GetMethod('SomeHelper'); // rmethod is nil !
end;

那么 RTTI 不适用于类助手中定义的方法吗?有没有办法解决?

谢谢。

4

1 回答 1

9

您的代码返回nil方法的原因是对象的类型不包含名为 的方法SomeHelper。包含该方法的类型是辅助类型。

所以,你可以这样写,它会返回一个非 nil 方法:

obj:=TSampleClass.Create;
rtype:=ctx.GetType(TypeInfo(TSampleClassHelper));
rmethod:=rtype.GetMethod('SomeHelper');

当然,您应该立即看到第一个问题,即使用编译时指定类型,TSampleClassHelper. 我们可以使用 RTTITSampleClassHelper在运行时根据实例的类型进行发现吗?不,我们不能,我将在下面解释。

即使我们把它放在一边,据我所知,没有办法使用 RTTI 调用该方法。如果您调用rmethod.Invoke(obj, []),则代码中的代码会TRttiInstanceMethodEx.DispatchInvoke阻止尝试调用辅助方法。它阻止它,因为它规定实例的类型与方法的类不兼容。相关代码是:

if (cls <> nil) and not cls.InheritsFrom(TRttiInstanceType(Parent).MetaclassType) then
  raise EInvalidCast.CreateRes(@SInvalidCast);

好吧,您可以获取辅助方法的代码地址,rmethod.CodeAddress但您需要找到其他方法来调用该方法。将其转换为具有适当签名的方法并调用它很容易。但是为什么要rmethod.CodeAddress在任何情况下打扰呢?为什么不使用TSomeHelperClass.SomeMethodRTTI 并将其从循环中删除?

讨论

Helper 解析是基于编译时的活动助手静态执行的。一旦您尝试使用 RTTI 调用辅助方法,就没有活动的辅助方法。你早就完成编译了。所以你必须决定使用哪个助手类。此时,您不需要 RTTI。

这里的基本问题是类帮助方法解析基本上是使用编译器上下文执行的静态过程。由于在运行时没有编译器上下文,因此无法使用 RTTI 执行类帮助方法解析。

要更深入地了解这一点,请阅读 Allen Bauer 的回答:在运行时使用 RTTI 查找 Delphi 中的所有类助手?

于 2013-06-21T20:16:27.093 回答