Lets say I have a sample class helper
TSampleClassHelper = class helper for TSampleClass
public
procedure SomeHelper;
end;
I do the following:
var
obj :TSampleClass;
begin
obj:=TSampleClass.Create;
obj.SomeHelper;
end;
and this works as expected.
But how can I use RTTI to invoke the helper method instead? The following does not seem to work, GetMethod
returns 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;
So does RTTI not work for methods defined in class helpers? Is there anyway around this?
Thanks.
The reason your code returns a nil
method is that the object's type does not contain a method named SomeHelper
. The type that contains that method is the helper type.
So, you could write this which will return a non-nil method:
obj:=TSampleClass.Create;
rtype:=ctx.GetType(TypeInfo(TSampleClassHelper));
rmethod:=rtype.GetMethod('SomeHelper');
Of course, you should immediately see the first problem, namely the use of a compile time specified type, TSampleClassHelper
. Can we use RTTI to discover TSampleClassHelper
at run time based on the type of the instance? No we cannot, as I will explain below.
Even if we put that to one side, as far as I can see, there's no way to invoke the method using RTTI. If you call rmethod.Invoke(obj, [])
then the code in TRttiInstanceMethodEx.DispatchInvoke
blocks an attempt to call the helper method. It blocks it because it decrees that the type of the instance is not compatible with the class of the method. The pertinent code is:
if (cls <> nil) and not cls.InheritsFrom(TRttiInstanceType(Parent).MetaclassType) then
raise EInvalidCast.CreateRes(@SInvalidCast);
Well, you can obtain the code address of the helper method with rmethod.CodeAddress
but you'll need to find some other way to invoke that method. It's easy enough to cast it to a method with the appropriate signature and invoke it. But why bother with rmethod.CodeAddress
in any case? Why not use TSomeHelperClass.SomeMethod
and cut RTTI out of the loop?
Discussion
Helper resolution is performed statically based on the active helper at the point of compilation. Once you attempt to invoke a helper method using RTTI there is no active helper. You've long since finished compiling. So you have to decide which helper class to use. At which point, you don't need RTTI.
The fundamental issue here is that class helper method resolution is fundamentally a static process performed using the context of the compiler. Since there is not compiler context at run time, class helper method resolution cannot be performed using RTTI.
For more insight into this have a read of Allen Bauer's answer here: Find all Class Helpers in Delphi at runtime using RTTI?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With