Сначала небольшое объяснение моей ситуации:
У меня есть пример интерфейса, который реализован различными классами, и у этих классов не всегда может быть общий предок:
IMyInterface = interface
['{1BD8F7E3-2C8B-4138-841B-28686708DA4D}']
procedure DoSomething;
end;
TMyImpl = class(TInterfacedPersistent, IMyInterface)
procedure DoSomething;
end;
TMyImp2 = class(TInterfacedObject, IMyInterface)
procedure DoSomething;
end;
У меня также есть метод фабрики, который должен создать экземпляр объекта, который реализует мой интерфейс. Мой фабричный метод получает имя класса в качестве параметра:
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, но у него все еще есть проблема утечки памяти; Объект, созданный здесь, не привязан ни к какой переменной интерфейса; следовательно, он не подсчитывается, а протекает.
.
Теперь мой актуальный вопрос:
Итак, мой вопрос: как я могу безопасно создать экземпляр класса, который реализует мой интерфейс во время выполнения? Я видел Delphi Spring Framework, и он предоставляет такую функциональность в своем модуле Spring.Services, но у него есть свои собственные процедуры отражения и модели управления временем жизни. Я ищу легкое решение, а не целый сторонний фреймворк, чтобы сделать это для меня.
Привет