Бросок интерфейса Delphi, использующий TValue

Я недавно экспериментировал экстенсивно с интерфейсами и D2010 RTTI. Я не знаю во времени выполнения фактический тип интерфейса; хотя у меня будет доступ к, он - полностью определенное имя с помощью строки.

Рассмотрите следующее:

program rtti_sb_1;
{$APPTYPE CONSOLE}
uses
  SysUtils, Rtti, TypInfo, mynamespace in 'mynamespace.pas';
var
  ctx:                  TRttiContext;
  InterfaceType:        TRttiType;
  Method:               TRttiMethod;
  ActualParentInstance: IParent;
  ChildInterfaceValue:  TValue;
  ParentInterfaceValue: TValue;
begin
  ctx := TRttiContext.Create;
  // Instantiation
  ActualParentInstance := TChild.Create as IParent;
  {$define WORKAROUND}
  {$ifdef WORKAROUND}
  InterfaceType := ctx.GetType(TypeInfo(IParent));
  InterfaceType := ctx.GetType(TypeInfo(IChild));
  {$endif}
  // Fetch interface type
  InterfaceType := ctx.FindType('mynamespace.IParent');
  // This cast is OK and ChildMethod is executed
  (ActualParentInstance as IChild).ChildMethod(100);
  // Create a TValue holding the interface
  TValue.Make(@ActualParentInstance, InterfaceType.Handle, ParentInterfaceValue);
  InterfaceType := ctx.FindType('mynamespace.IChild');
  // This cast doesn't work
  if ParentInterfaceValue.TryCast(InterfaceType.Handle, ChildInterfaceValue) then begin
    Method := InterfaceType.GetMethod('ChildMethod');
    if (Method <> nil) then begin
      Method.Invoke(ChildInterfaceValue, [100]);
    end;
  end;
  ReadLn;
end.

Содержание mynamespace.pas следующие:

{$M+}
IParent = interface
  ['{2375F59E-D432-4D7D-8D62-768F4225FFD1}']
  procedure ParentMethod(const Id: integer);
end;
{$M-}
IChild = interface(IParent)
  ['{6F89487E-5BB7-42FC-A760-38DA2329E0C5}']
  procedure ChildMethod(const Id: integer);
end;
TParent = class(TInterfacedObject, IParent)
public
  procedure ParentMethod(const Id: integer);
end;
TChild = class(TParent, IChild)
public
  procedure ChildMethod(const Id: integer);
end;

Для полноты реализация идет как

procedure TParent.ParentMethod(const Id: integer);
begin
  WriteLn('ParentMethod executed. Id is ' + IntToStr(Id));
end;
procedure TChild.ChildMethod(const Id: integer);
begin
  WriteLn('ChildMethod executed. Id is ' + IntToStr(Id));
end;

Причина {$define WORKAROUND} может быть найден в этом сообщении.

Вопрос: есть ли какой-либо способ для меня сделать желаемый бросок типа, использующий RTTI? Другими словами: есть ли способ для меня вызвать IChild. ChildMethod от знания 1) полностью определенного имени IChild как строка и 2) ссылки на экземпляр TChild как интерфейс IParent? (В конце концов, трудно кодированный бросок хорошо работает. Это даже возможно?) Спасибо!

1
задан Community 23 May 2017 в 12:18
поделиться

1 ответ

Это выглядит довольно уродливым примером ленивого кодирования в RTTI.pas. В функции ConvIntf2Intf , которая заботится о приведении интерфейсов в TValue, она явно проверяет только приведение к IInterface . Любой другой интерфейс автоматически вернет false. Он может легко извлечь GUID (если он есть в вашем интерфейсе) и попытаться выполнить вызов QueryInterface, но по какой-то причине он этого не делает. Я бы доложил об этом в QC.

2
ответ дан 2 September 2019 в 23:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: