Создание экземпляра средства реализации интерфейса во время выполнения

Во-первых, небольшое объяснение моей ситуации:

У меня есть образец интерфейса, который реализуется разными классами, и эти классы не всегда могут иметь общего предка:

  IMyInterface = interface
    ['{1BD8F7E3-2C8B-4138-841B-28686708DA4D}']
    procedure DoSomething;
  end;

  TMyImpl = class(TInterfacedPersistent, IMyInterface)
    procedure DoSomething;
  end;

  TMyImp2 = class(TInterfacedObject, IMyInterface)
    procedure DoSomething;
  end;

У меня также есть фабричный метод, который предполагается o создать экземпляр объекта, реализующего мой интерфейс. Мой фабричный метод получает имя класса в качестве параметра:

  function GetImplementation(const AClassName: string): IMyInterface;

Я пробовал два подхода к реализации этого фабричного метода, первый из них использовал расширенный RTTI:

var
  ctx : TRttiContext;
  t : TRttiInstanceType;
begin
  t := ctx.FindType(AClassName).AsInstance;
  if Assigned(t) then
    Result := t.GetMethod('Create').Invoke(t.MetaclassType, []).AsInterface as IMyInterface;
end;

В этом подходе я вызываю конструктор по умолчанию, который подходит для моего сценария. . Проблема в том, что во время выполнения я получаю сообщение об ошибке, сообщающее мне, что объект не поддерживает IMyInterface. Более того, созданный объект не назначается интерфейсной переменной; следовательно, это будет утечка. Я также попытался вернуть значение с помощью метода TValue.AsType, но он дал мне нарушение прав доступа:

function GetImplementation(const AClassName: string): IMyInterface;
var
  ctx : TRttiContext;
  rt : TRttiInstanceType;
  V : TValue;
begin
  rt := ctx.FindType(AClassName).AsInstance;
  if Assigned(rt) then
  begin
    V := rt.GetMethod('Create').Invoke(rt.MetaclassType, []);
    Result := V.AsType<IMyInterface>;
  end;
end;

.

Второй подход, который я пробовал, заключался в использовании универсального словаря для хранения пар и обеспечения регистрации, методов отмены регистрации:

  TRepository = class
  private
    FDictionary : TDictionary<string, TClass>;
  public
    constructor Create;
    destructor Destroy; override;
    function GetImplementation(const AClassName: string): IMyInterface;
    procedure RegisterClass(AClass: TClass);
    procedure UnregisterClass(AClass: TClass);
  end;

Здесь я реализовал метод GetImplementation следующим образом:

function TRepository.GetImplementation(const AClassName: string): IMyInterface;
var
  Obj : TObject;
begin
  if FDictionary.ContainsKey(AClassName) then
  begin
    Obj := FDictionary[AClassName].Create;
    Obj.GetInterface(IMyInterface, Result);
  end;
end;

Это отлично работает, и я могу вызвать метод DoSomething, используя возвращенное значение GetImplementation, но у него все еще есть проблема с утечкой памяти; Созданный здесь Obj не привязан ни к какой интерфейсной переменной; следовательно, он не подсчитывается по ссылкам и утекает.

.

Теперь мой настоящий вопрос:

Итак, мой вопрос: как я могу безопасно создать экземпляр класса, который реализует мой интерфейс во время выполнения? Я видел Delphi Spring Framework, и он предоставляет такую ​​функциональность в своем модуле Spring.Services, но имеет свои собственные процедуры отражения и модели управления жизненным циклом. Я ищу легкое решение, а не целую стороннюю платформу, чтобы сделать это за меня.

С уважением

10
задан RRUZ 6 December 2011 в 21:28
поделиться