Delphi 类助手 RTTI GetMethod

8

假设我有一个样本类helper

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

我做以下工作:

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

这段代码预期结果如下。

但是如何使用RTTI调用辅助方法呢?以下尝试不起作用,GetMethod返回值为nil。

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对于类助手中定义的方法无效吗?有没有什么解决方法?
谢谢。

我明白了,但在我的实际代码中,我正在针对任意对象测试'SomeMethod'。我不知道这个对象是否通过助手定义了该方法。所以我猜这对于通过类助手定义的'SomeMethod'是行不通的。唉,算了。 - AJ.
1个回答

10

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

因此,你可以编写以下代码来返回非nil方法:


obj:=TSampleClass.Create;
rtype:=ctx.GetType(TypeInfo(TSampleClassHelper));
rmethod:=rtype.GetMethod('SomeHelper');
当然,您应该立即看到第一个问题,即使用编译时指定的类型TSampleClassHelper。我们能否使用RTTI根据实例的类型在运行时发现TSampleClassHelper?不行,我将在下面解释。
即使我们把这个问题放在一边,据我所见,没有办法使用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.SomeMethod并切断RTTI的循环呢?

讨论

在编译时基于活动的帮助器执行帮助器解析。一旦尝试使用RTTI调用帮助方法,就没有“活动”帮助器了。编译已经完成很久了。因此,您必须决定使用哪个帮助类。此时,您不需要RTTI。

这里的根本问题是类帮助器方法解析在本质上是使用编译器上下文执行的静态过程。由于在运行时没有编译器上下文,所以不能使用RTTI执行类帮助器方法解析。

有关更多信息,请阅读Allen Bauer在这里的回答


并不是一个类有多个辅助程序,有些类是通过辅助程序定义所需的方法,而有些类则不是。例如,对于TStrings,我已经通过辅助程序添加了该方法,但对于TMyClass,它直接在类上定义。 - AJ.
@AJ。真正的问题在于编译器基于编译上下文静态执行帮助方法解析。这个概念与RTTI完全无关。 - David Heffernan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接