【Delphi】中通过索引动态定位并创建对应窗体类实例

        

        在Delphi的开发中,经常会遇到创建很多相同的类或者相似的类的实例,期望通过索引动态的定位这些实例。  

       例如: delphi 中,有几个类似窗体,例如TTest_Form1,TTest_Form2,TTest_Form3,TTest_Form4等,期望通过索引能够找到对应的类,并创建实例,例如知道索引是3,那么就会用TTest_Form3.create创建对象实例。      

一、使用类引用数组(Class of TForm 或者具体类的类引用)

  • 步骤:

    1. 定义一个类型别名,表示窗体类的类引用。对于具体窗体可以使用通用的 TTest_Form 的子类型,或直接使用 class of TForm
    2. 将各个窗体类的类型放入一个数组,数组下标与你希望的“索引”对应。
    3. 根据索引取出类引用,然后调用 Create 构造对象(通常需要传入所有权所有者,如 Owner)。
    4. 可选:统一初始化、显示、释放等流程。
  • 代码示例(假设窗体都继承自 TForm,且构造参数为 Owner: TComponent):

type
  // 窗体类的通用类引用类型
  TFormClass = class of TForm;

  // 如果你确实只有 TTest_Form1..TTest_Form4 这几个
  // 也可以直接用 TFormClass,数组中存放它们的类引用
// 在某个单位的实现部分,或者一个工厂单元里
const
  // 注意下标从 0 开始,或者按你需要的起始下标调整
  // 将 1: TTest_Form1, 2: TTest_Form2, ...
var
  FormClasses: array of TFormClass;
  idx: Integer;
  NewForm: TForm;
begin
  // 初始化类引用数组(只需在程序启动阶段或需要时设置一次)
  SetLength(FormClasses, 4);
  FormClasses[0] := TTest_Form1;
  FormClasses[1] := TTest_Form2;
  FormClasses[2] := TTest_Form3;
  FormClasses[3] := TTest_Form4;

  // 假设索引为 3,想创建 TTest_Form3 的实例
  idx := 3 - 1; // 根据你使用的索引起始,若你希望索引直接对应数组下标,则不需要 -1

  if (idx >= 0) and (idx < Length(FormClasses)) and Assigned(FormClasses[idx]) then
  begin
    NewForm := FormClasses[idx].Create(Application); // 根据你的Owner 调整
    try
      NewForm.Show; // 或者 ShowModal、BringToFront 等
    except
      NewForm.Free;
      raise;
    end;
  end
  else
    raise Exception.Create('Invalid form index');
end;
  • 注意事项:
    • 管理 Owner:通常传 Application 或特定父窗体。若直接创建非可视窗体,确保正确管理生命周期。
    • 异常处理:创建失败时要释放资源,避免内存泄漏。
    • 界面释放:根据窗体的用途,选择 FreeFreeAndNil,以及是否需要在显示后再释放(如模态对话框在返回后自行释放,需自行管理)。

二、使用工厂函数表(更灵活、便于扩展)

  • 定义一个工厂函数类型,返回 TForm,并把各窗体的工厂函数放入数组。
  • 优点:若窗体需要自定义初始化参数,可以在工厂函数中完成。
type
  TFormFactory = function: TForm of object;

function CreateTestForm1: TForm;
begin
  Result := TTest_Form1.Create(Application);
  // 额外初始化
end;

function CreateTestForm2: TForm;
begin
  Result := TTest_Form2.Create(Application);
end;

// 依此类推
var
  FormFactories: array of TFormFactory;
  idx: Integer;
  FormObj: TForm;
begin
  SetLength(FormFactories, 4);
  FormFactories[0] := @CreateTestForm1;
  FormFactories[1] := @CreateTestForm2;
  // ...
  idx := 3 - 1;

  if (idx >= 0) and (idx < Length(FormFactories)) and Assigned(FormFactories[idx]) then
  begin
    FormObj := FormFactories[idx]();
    try
      FormObj.Show;
    except
      FormObj.Free;
      raise;
    end;
  end
  else
    raise Exception.Create('Invalid form index');
end;

三、使用 RTTI 动态创建

  • 如果你的窗体命名规则严格(如 TTest_Form1, TTest_Form2,...),也可以利用 RTTI 根据名称动态创建。
  • 但这会涉及到运行时类型信息和字符串拼接,容易出错且效率低。

示例(简化,实际使用需引入 RTTI 单元):

uses
  Rtti, TypInfo;

function CreateFormByIndex(Index: Integer; Owner: TComponent): TForm;
var
  Ctx: TRttiContext;
  Typ: TRttiType;
  TypeName: string;
  FormInstance: TValue;
begin
  // 根据索引映射到类名,如 3 -> 'TTest_Form3'
  TypeName := Format('TTest_Form%d', [Index]);
  Typ := Ctx.FindType(TypeName);
  if Typ = nil then
    raise Exception.Create('Unknown form type: ' + TypeName);
  if not (Typ is TRttiInstanceType) then
    raise Exception.Create(TypeName + ' is not a class type');
  // 创建实例:需要知道构造参数,此处假设有无参构造且 Owner 通过参数传入
  // 由于 RTTI 创建无参构造较复杂,通常不推荐直接用 RTTI 动态创建窗体
  Result := nil; // 更复杂实现略
end;

总结与推荐

  • 最稳妥、简单且可维护的方式,是将窗体类引用放入一个数组(方法 A),或用工厂函数表(方法 B),按索引选择创建。这样编译期明确、性能好、容易调试。
  • 如果未来窗体数量极大且经常新增,方法 B(工厂函数表)会更易扩展,因为你只需添加一个新的工厂函数并更新数组。
  • 在使用时,注意:同一位置的索引应与数组下标或你的业务逻辑严格对应,若要从 1 开始索引,记得做相应的偏移处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

海纳老吴

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值