Delphi Class Helper RTTI GetMethod
Допустим, у меня есть образец помощника класса
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 не работает для методов, определенных в помощниках класса? Есть ли что-нибудь вокруг этого?
Благодарю.
1 ответ
Причина, по которой ваш код возвращает nil
Метод заключается в том, что тип объекта не содержит метод с именем SomeHelper
, Тип, содержащий этот метод, является вспомогательным типом.
Итак, вы можете написать это, который будет возвращать не ноль метод:
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.
Чтобы узнать больше об этом, прочитайте ответ Аллена Бауэра здесь: Найти всех помощников классов в Delphi во время выполнения, используя RTTI?