【问题标题】:Creating Delphi Objects at runtime based on class type基于类类型在运行时创建 Delphi 对象
【发布时间】:2011-02-01 20:24:38
【问题描述】:

是否可以通过调用方法在运行时根据对象的类型创建对象。

我想要实现的是

var
  lForm1 : TForm;
  lForm2 : TForm;
begin
  CreateObjects([lForm1, lForm2]);
  // After this call I have the variables initialized and I can use them.
end;

【问题讨论】:

  • 哪个 Delphi 版本? D2010 从上到下彻底检查了 RTTI,这可能会改变您需要这样做的方式。

标签: delphi runtime


【解决方案1】:

问题中的信息不足。

表单对象(在问题中)的“类型”从何而来?它只是一个类型名称吗? CreateObjects() 如何发现每个对象所需的类型?

它不能来自传入的对象引用的“类型”,因为这可能(并且几乎肯定会,如您的示例)仅仅是所需的具体类型最终派生的基本类型。

如果没有关于您的具体实施目标和限制的更详细信息,就不可能给出完整、具体的答案。

但是,一般而言,您所寻求的可以通过虚拟构造函数和 VCL 提供的 RegisterClass / FindClass 基础设施的组合来实现。

简单来说,您将拥有一个基类,该基类引入了用于实例化您的类的通用构造函数 [对于 TComponent 派生类,这已经以 Create(所有者:TComponent)构造函数]。

然后,您可以在运行时使用 FindClass('TClassName') 获取对任何(注册的)类的引用。这将返回一个类引用,然后您可以使用它调用适当的虚拟构造函数:

  type
    TFoo = class ....
    TFooClass = class of TFoo;

    // etc


  var
    someClass: TFooClass;
    someObj: TFoo;
  begin
    someClass := TFooClass(FindClass('TFooDerivedClass'));
    someObj := someClass.Create(nil);
      :

请注意,TFooDerivedClass 是一个最终派生自 TFooClass 的类(为了简单起见,假设依次派生自 TComponent并且在这种情况下用 NIL 所有者实例化)。可以使用 FindClass() 找到已经在类型系统中注册的类。这包括应用程序中某些DFM 引用的任何控件或组件类。任何需要注册的附加类都可以使用 RegisterClass() 显式注册。

您的特定应用程序如何识别所涉及的对象类型以及类型名称到其他任意识别系统的任何映射是您必须注意的实现细节。

【讨论】:

  • 你可以调用适当的non-virtual构造函数并获得相同的效果。虚拟构造函数和类引用是相关但不相互依赖的概念。
  • 仅注册TFoo 将找不到TFooDerivedClass。我必须同时注册每个TFooDerivedClass 吗?
  • @kobik 是的。 FindClass() 只能查找已使用 RegisterClass() 注册的类。注册基类并不会自动注册所有子类(基类甚至不知道可能存在哪些子类!)
【解决方案2】:

未经测试的概念代码:

function instantiate(var instancevars : array of tobject;
              const classtypes : array of TBaseClassType):boolean;

begin
  if (length(instancevars)=0) or (length(instancevars)<>length(classtypes)) then
    exit(false);
  for i:=0 to length(instancevars)-1 do
     instancevars[i]:=classtypes[i].create;
  result:=true;
end;

然后使用

instantiate([lform1,lform2],[tform1,tform2]); 

让它工作。

请注意,“TBaseClassType”必须是用于此功能的所有类的某个基类,并且具有虚拟构造函数(例如 TPersistent?)。可能您还需要更正 .create 行(例如 add (NIL) )

你无法从变量中获取类型,该信息仅在编译时可用。

【讨论】:

  • 只有当后代类需要在构造函数中做不同的事情时,构造函数才需要是虚拟的。使用非虚拟构造函数仍将创建正确类型的实例,并且所有其他虚拟方法仍将被正确调用,包括AfterConstruction
  • 您可以将 TBaseClassType 更改为 TObject,它将适用于任何类。
  • @skamradt 覆盖的构造函数不会被调用
  • 我知道,我只是不想让它看起来过于宽泛,但有限制。
【解决方案3】:

引用您对 Henk 回答的评论:

那是我不想做的。我有很多服务器端方法,我在运行时创建了很多控件,我想知道创建上面的对象会减少代码。

“很多”是什么意思?

如果您的意思是许多完全相同类型的组件(例如:“but1, but2, but3, .. but55 : TButton;”)然后更改您的代码并使用数组来表示您的变量 - 然后您可以一个简单的循环来创建它们。

如果您指的是很多不同类型的组件(例如:but1 : TAnimatedButton; but2 : TFlatButton; but3 : T3DButton;),我看不到任何简单的方法可以做到这一点,我认为你会创建一个小的调试地狱比什么都重要。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-10-31
    • 1970-01-01
    • 2013-07-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多