【问题标题】:How can I create an Delphi object from a class reference and ensure constructor execution?如何从类引用创建 Delphi 对象并确保构造函数执行?
【发布时间】:2010-10-21 22:07:40
【问题描述】:

如何使用类引用创建对象的实例,以及 确保构造函数被执行?

在此代码示例中,不会调用 TMyClass 的构造函数:

type
   TMyClass = class(TObject)
     MyStrings: TStrings;
     constructor Create; virtual;
   end;

constructor TMyClass.Create;
begin
   MyStrings := TStringList.Create;
end;

procedure Test;
var
   Clazz: TClass;
   Instance: TObject;
begin
   Clazz := TMyClass;
   Instance := Clazz.Create;
end;

【问题讨论】:

    标签: delphi class constructor reference delphi-2009


    【解决方案1】:

    使用这个:

    type
      TMyClass = class(TObject)
        MyStrings: TStrings;
        constructor Create; virtual;
      end;
      TMyClassClass = class of TMyClass; // <- add this definition
    
    constructor TMyClass.Create;
    begin
       MyStrings := TStringList.Create;
    end;
    
    procedure Test;
    var
      Clazz: TMyClassClass; // <- change TClass to TMyClassClass
      Instance: TObject;
    begin
       Clazz := TMyClass; // <- you can use TMyClass or any of its child classes. 
       Instance := Clazz.Create; // <- virtual constructor will be used
    end;
    

    或者,您可以使用 TMyClass 的类型转换(而不是“TMyClass 的类”)。

    【讨论】:

    • 好的,如果我理解正确,这意味着如果我想用 Delphi 构建一个通用对象工厂,我需要将“TMyClass 的类”分配给一个变量——但这似乎是不可能的。跨度>
    • 如果你想构造某种类型的对象,那么你需要有类的类型信息。如果你没有类信息 - 你不能构造这种类型的对象。很明显;)
    【解决方案2】:

    Alexander 的解决方案很好,但在某些情况下还不够。假设您希望设置一个 TClassFactory 类,其中 TClass 引用可以在运行时存储,并在以后检索任意数量的实例。

    这样的类工厂永远不会知道它所拥有的类的实际类型,因此无法将它们转换为相应的元类。在这种情况下,要调用正确的构造函数,可以使用以下方法。

    首先,我们需要一个简单的演示类(不要介意公共字段,它只是用于演示目的)。

    interface
    
    uses
      RTTI;
    
    type
      THuman = class(TObject)
      public
        Name: string;
        Age: Integer;
    
        constructor Create(); virtual;
      end;
    
    implementation
    
    constructor THuman.Create();
    begin
      Name:= 'John Doe';
      Age:= -1;
    end;
    

    现在我们完全通过 RTTI 并使用正确的构造函数调用来实例化 THuman 类型的对象。

    procedure CreateInstance();
    var
      someclass: TClass;
      c: TRttiContext;
      t: TRttiType;
      v: TValue;
      human1, human2, human3: THuman;
    begin
      someclass:= THuman;
    
      // Invoke RTTI
      c:= TRttiContext.Create;
      t:= c.GetType(someclass);
    
      // Variant 1a - instantiates a THuman object but calls constructor of TObject
      human1:= t.AsInstance.MetaclassType.Create;
    
      // Variant 1b - same result as 1a
      human2:= THuman(someclass.Create);
    
      // Variant 2 - works fine
      v:= t.GetMethod('Create').Invoke(t.AsInstance.MetaclassType,[]);
      human3:= THuman(v.AsObject);
    
      // free RttiContext record (see text below) and the rest
      c.Free;
    
      human1.Destroy;
      human2.Destroy;
      human3.Destroy;
    end;
    

    你会发现对象“human1”和“human2”已经被初始化为零,即Name=''和Age=0,这不是我们想要的。对象 human3 保存了 THuman 的构造函数中提供的默认值。

    但是请注意,此方法要求您的类具有不带参数的构造方法。以上所有内容都不是我构思的,而是在Rob Love's Tech Corner 中进行了精彩且更详细的解释(例如,c.Free 部分)。

    【讨论】:

      【解决方案3】:

      请检查是否可以选择覆盖 AfterConstruction。

      【讨论】:

      • 好主意,它是 TObject 中的一个虚方法,所以我不需要添加任何合成的新根类。为这个想法 +1。
      【解决方案4】:

      您的代码稍作修改:

      type
        TMyObject = class(TObject)
          MyStrings: TStrings;
          constructor Create; virtual;
        end;
        TMyClass = class of TMyObject;
      
      constructor TMyObject.Create;
      begin
        inherited Create;
        MyStrings := TStringList.Create;
      end;
      
      procedure Test; 
      var
        C: TMyClass;
        Instance: TObject;
      begin
         C := TMyObject;
         Instance := C.Create;
      end;
      

      【讨论】:

        【解决方案5】:

        您可以在基类中创建一个抽象方法,并在构造函数中调用它,并在从类引用创建时在要执行的子类中覆盖。

        【讨论】:

        • 这不是问题所要求的。
        • 我遇到了同样的问题,当我在一些列表管理器的构造函数中传递类引用时,指定它填充列表的类型,以及当列表创建孩子时,只有子类构造函数的父基类被调用...子项的构造函数不是...对我来说唯一的解决方案,因为构造函数必须有参数,是声明一个抽象方法,在父构造函数中调用它,并在儿童班。这种解决方法可能可以帮助遇到我们同样问题的人......最好的问候!
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-03-04
        • 2014-05-23
        • 1970-01-01
        • 2013-09-30
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多