首先,有关我的情况的一点解释:
我有一个由不同类实现的示例接口,这些类可能并不总是具有共享祖先:
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;
这样做很好,我可以使用GetImplementation的返回值调用DoSomething方法,但是它仍然具有内存泄漏问题;这里创建的Obj没有分配给任何接口变量;因此,它不是引用计数,而是泄漏.
.
现在我的实际问题是:
所以我的问题是,如何安全地创建一个在运行时实现我的接口的类的实例?我看到Delphi Spring框架,它在Spring.Services单元中提供了这样的功能,但它有自己的反射例程和终身管理模型.我正在寻找一个轻量级的解决方案,而不是一个完整的第三方框架来为我做这个.
问候
解决方法
使用RTTI的第一种情况会给您一个访问冲突,因为TRttiContext.FindType(AClassName)找不到没有注册或在应用程序中显示的类的Rtti信息.
所以你可以改变你的代码
function GetImplementation(AClass: TClass): IMyInterface; var ctx : TRttiContext; t : TRttiInstanceType; begin t := ctx.GetType(AClass).AsInstance; if Assigned(t) then Result := t.GetMethod('Create').Invoke(t.MetaclassType,[]).AsInterface As IMyInterface; end;
并以这种方式打电话
AClass:=GetImplementation(TMyImp2);
现在,如果要使用类名调用该类,使用列表(如您的TRepository类)来注册类是一个有效的aproach.关于内存泄漏我很确定是因为TMyImpl类派生自TInterfacedPersistent,它不直接像TInterfacedObject一样实现引用计数.
TRepository的这个实现必须正常.
constructor TRepository.Create; begin FDictionary:=TDictionary<string,TClass>.Create; end; destructor TRepository.Destroy; begin FDictionary.Free; inherited; end; 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; { or using the RTTI var ctx : TRttiContext; t : TRttiInstanceType; begin t := ctx.GetType(FDictionary[AClassName]).AsInstance; if Assigned(t) then Result := t.GetMethod('Create').Invoke(t.MetaclassType,[]).AsInterface As IMyInterface; end; } procedure TRepository.RegisterClass(AClass: TClass); begin FDictionary.Add(AClass.ClassName,AClass); end; procedure TRepository.UnregisterClass(AClass: TClass); begin FDictionary.Remove(AClass.ClassName); end;