You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)))写法:

  1. TClass(Self.FInstance)会把实例转为TClass类型,调用的是TObject.Free静态方法,而非TFirst中自定义的Free方法
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 16:44:50