Использование ссылки на функцию, возвращаемой вызовом метода RTTI - PullRequest
0 голосов
/ 20 ноября 2018

В программе на Delphi используйте следующий шаблон:

TDelegate=reference to procedure(const Arg: TMyType); 

TRouter = class
  ...
public
  procedure RegisterHandler(const route: string: handler: TDelegate);
end;

THandlerContainer = class
public
  function getDelegate: TDelegate;
  procedure register(const Router: TRouter);
end; // class

...
procedure THandlerContainer.register(const router: TRouter)
begin
  router.RegisterHandler('route', getDelegate);
end;

По сути, я регистрирую ссылки на функции, которые будут использоваться для обработки некоторых сообщений (на основе строки «route»).

Я хотел бы упростить шаблон для моих коллег, чтобы им не нужно было вызывать router.RegisterHandler для каждой реализации, а просто добавить атрибут к своему классу, а затем передать экземпляр в метод TRouter, которыйбудет использовать RTTI, чтобы найти все методы, оформленные этим атрибутом, и зарегистрировать их.

Поэтому я создал простой атрибут RegisterMessageHandlerAttribute для этого украшения (с помощью специального конструктора для получения строки маршрутизации) и написал методTRouter, который использует RTTI, чтобы найти все методы, украшенные этим атрибутом:

function TRouter.RegisterHandlers(const HandlerContainerClass:
    TObject);
var
  RTTIContext: TRttiContext;
  RttiType : TRttiType;
  prop: TRttiMethod;
  Attr: TCustomAttribute;
begin
  RTTIContext := TRttiContext.Create;
  try
    RttiType := RTTIContext.GetType(HandlerContainerClass);
    if assigned(RttiType) then
    begin
      for prop in RttiType.GetMethods do
      begin
         for Attr in Prop.GetAttributes do
         begin
           if (Attr is RegisterMessageHandlerAttribute) then
           begin
               Self.RegisterHandler(
                (Attr as RegisterMessageHandlerAttribute).Route,
                TDelegate(Prop.Invoke(HandlerContainerClass, []).AsPointer); // <--- this fails
               );
           end;
         end;
      end;
    end;
  finally
    RTTIContext.Free;
  end;
  result := Handlers.ToArray;
end;

К сожалению, компиляторы жалуются на строку, где я извлекаю лямбда, вызывая метод:

TDelegate(Prop.Invoke(HandlerContainerClass, []).AsPointer);
...
[dcc32 Error] GIT.MessageQueue.Router.pas(169): E2089 Invalid typecast

Моя проблема в том, что я понятия не имею, как взять тип TValue, возвращенный Prop.Invoke и используйте его как ссылку на функцию типа TDelegate.

1 Ответ

0 голосов
/ 20 ноября 2018

Просто используйте .AsType<TDelegate>() - это возвращает содержимое TValue как TDelegate.Эта функция также гарантирует, что вы не превратите что-то внутри TValue во что-то, что не является явно совместимым с присваиванием (не так, как это делают варианты).Но так как это точный тип возвращаемого значения вашей функции, он просто будет работать.

PS Вам необходимо явно ввести круглые скобки, потому что в противном случае вы можете получить ошибку E2010 от компилятора.

...