[英]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.