Delphi泛型单例中TFirst.Free未触发,如何无需强制转换解决?
问题:泛型单例中ReleaseInstance无法触发TFirst的Free方法
调用ReleaseInstance()方法时,TFirst类的Free()方法始终不会触发。需要在不强制进行TFirst类型转换的前提下解决该问题,曾尝试使用RTTI但未成功。
以下写法可生效,但不符合泛型使用规范:
TFirst(Self.FInstance).Free;
完整代码示例:
unit Unit1; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls; type TSingleton<T: class, constructor> = class strict private class var FInstance: T; public class function GetInstance: T; class procedure ReleaseInstance; end; type TFirst = class public procedure Free; end; type TSecond = class(TFirst) end; type TMySingleton = TSingleton<TSecond>; type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} { TSingleton<T> } class function TSingleton<T>.GetInstance: T; begin if not Assigned(Self.FInstance) then Self.FInstance := T.Create; Result := Self.FInstance; end; class procedure TSingleton<T>.ReleaseInstance; begin if Assigned(Self.FInstance) then FreeAndNil(T(TClass(Self.FInstance))); end; { TFirst } procedure TFirst.Free; begin // It's not called inherited Free; end; procedure TForm1.Button1Click(Sender: TObject); begin TMySingleton.GetInstance; TMySingleton.ReleaseInstance; end; end.
问题根源
问题出在ReleaseInstance中的FreeAndNil(T(TClass(Self.FInstance)))写法:
TClass(Self.FInstance)会把实例转为TClass类型,调用的是TObject.Free静态方法,而非TFirst中自定义的Free方法TObject.Free并非虚方法,子类重新声明的Free无法通过父类类型触发
解决方案
方案一:修改泛型约束并直接调用实例的Free方法
将TSingleton的泛型约束改为限定T继承自TFirst,这样可以直接通过泛型实例调用TFirst的Free方法:
type TSingleton<T: TFirst, constructor> = class strict private class var FInstance: T; public class function GetInstance: T; class procedure ReleaseInstance; end; // ... class procedure TSingleton<T>.ReleaseInstance; begin if Assigned(FInstance) then begin FInstance.Free; // 直接调用TFirst的Free方法 FInstance := nil; end; end;
此方案无需强制类型转换,符合泛型设计规范,且能确保触发TFirst.Free。
方案二:使用RTTI调用自定义Free方法(不修改泛型约束)
如果不想限定泛型约束,可以通过RTTI反射调用实例的Free方法:
uses System.Rtti; // ... class procedure TSingleton<T>.ReleaseInstance; var Context: TRttiContext; RttiType: TRttiInstanceType; FreeMethod: TRttiMethod; begin if Assigned(FInstance) then begin Context := TRttiContext.Create; try RttiType := Context.GetType(FInstance.ClassType) as TRttiInstanceType; FreeMethod := RttiType.GetMethod('Free'); if Assigned(FreeMethod) then FreeMethod.Invoke(FInstance, []); finally Context.Free; end; FInstance := nil; end; end;
该方案通过RTTI获取实例的Free方法并调用,无需修改泛型约束,也能触发TFirst.Free。
方案三:将TFirst.Free改为虚方法(需调整类设计)
虽然TObject.Free不是虚方法,但可以在TFirst中声明虚方法,子类重写,确保调用时触发正确的实现:
type TFirst = class public procedure Free; virtual; // 声明为虚方法 end; type TSecond = class(TFirst) public procedure Free; override; // 可选:子类重写 end; // ... class procedure TSingleton<T>.ReleaseInstance; begin if Assigned(FInstance) then begin FInstance.Free; // 此时会根据实例实际类型触发对应的虚方法 FInstance := nil; end; end;
此方案需要调整TFirst的类设计,但能从根本上确保方法调用的多态性。
内容的提问来源于stack exchange,提问作者Wesley Bobato
相关产品推荐
相关产品推荐

