繁体   English   中英

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

[英]How can I create an Delphi object from a class reference and ensure constructor execution?

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

在此代码示例中,将不会调用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;

用这个:

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的类”)。

亚历山大的解决方案是一个很好的解决方案,但在某些情况下还不够。 假设您希望建立一个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的Tech Corner中进行了精彩而详尽的解释(例如,c.Free部分)。

请检查是否可以重写AfterConstruction。

您的代码稍作修改:

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;

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM