我有这样的代码
name := 'Foo';
If name = 'Foo' then
result := TFoo.Create
else if name = 'Bar' then
result := TBar.Create
else if name = 'FooFoo' then
result := TFooFoo.Create;
有没有办法
result := $name.create
或基于变量值创建类的某种方式?
所有类都扩展了相同的基类。
答案 0 :(得分:27)
从Delphi 2010开始,增强的RTTI允许您在不必创建自己的类注册表的情况下执行此操作。
使用RTTI
单位,您有多种选择。
对于参数较少的构造函数,最简单的一个是。
var
C : TRttiContext;
O : TObject;
begin
O := (C.FindType('UnitName.TClassName') as TRttiInstanceType).MetaClassType.Create;
...
end;
以下是使用TRttiMethod.Invoke()
var
C : TRttiContext;
T : TRttiInstanceType;
V : TValue;
begin
T := (C.FindType('StdCtrls.TButton') as TRttiInstanceType);
V := T.GetMethod('Create').Invoke(T.metaClassType,[self]);
(V.AsObject as TWinControl).Parent := self;
end;
我在RTTI
单元上写了several articles,因为有很多选项。
已更新基于David请求:
使用类类型(虚拟构造函数)与TRttiType.Invoke
我个人觉得每个都有不同的用途。如果我知道前面的所有类型,我使用类类型方法。
答案 1 :(得分:16)
您可以使用GetClass
功能,但必须先使用RegisterClass
或RegisterClasses
方法注册课程。
GetClass(const AClassName: string): TPersistentClass;
答案 2 :(得分:10)
执行此操作的常规方法是使用虚拟构造函数。一个很好的例子是TComponent
,你无疑是熟悉的。
TComponent
具有以下构造函数:
constructor Create(AOwner: TComponent); virtual;
另一个关键是TComponentClass
,它被声明为class of TComponent
。
当VCL流式传输.dfm文件时,它会从.dfm文件中读取该类的名称,并且通过我们不需要在此处覆盖的某个过程,将该名称转换为变量,ComponentClass
说类型为TComponentClass
。然后它可以使用:
Component := ComponentClass.Create(Owner);
这是拥有虚拟构造函数的一大优势,我鼓励您采用相同的方法。
如果必须使用字符串来标识类,那么您仍然需要提供一个查找例程来将字符串类名转换为类引用。如果方便的话,您可以使用TComponent
使用的相同VCL机制,即RegisterClass
。
或者,如果您可以使用类引用替换代码中的name
,那么您可以编写:
type
TFoo = class
constructor Create; virtual;
end;
TBar = class(TFoo);
TFooClass = class of TFoo;
var
MyClass: TFooClass;
...
MyClass := TFoo;
result := MyClass.Create;//creates a TFoo;
MyClass := TBar;
result := MyClass.Create;//creates a TBar;