如何从类引用创建Delphi对象并确保构造函数执行?

mjn*_*mjn 20 delphi constructor reference class delphi-2009

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

在此代码示例中,将不会调用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;
Run Code Online (Sandbox Code Playgroud)

Ale*_*lex 26

用这个:

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;
Run Code Online (Sandbox Code Playgroud)

或者,您可以使用类型转换为TMyClass(而不是"TMyClass类").


小智 22

亚历山大的解决方案很好,但在某些情况下还不够.假设您希望设置一个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;
Run Code Online (Sandbox Code Playgroud)

现在我们纯粹通过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;
Run Code Online (Sandbox Code Playgroud)

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

但请注意,此方法要求您的类具有不带参数的构造方法.以上所有内容都不是由我构思出来的,而是在Rob Love的技术角中更加精细地解释(例如,c.免费部分).


Sti*_*ers 11

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


Ond*_*lle 7

您的代码略有修改:

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;
Run Code Online (Sandbox Code Playgroud)