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?

Другие вопросы по тегам