Delphi - 从字符串创建类

Wiz*_*ard 17 delphi oop object

我有这样的代码

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

有办法吗?

result := $name.create
Run Code Online (Sandbox Code Playgroud)

或者某种基于变量值创建类的方法?

所有类都扩展了相同的基类.

Rob*_*ove 28

从Delphi 2010开始,增强的RTTI允许您在不必创建自己的类注册表的情况下执行此操作.

使用该RTTI单元,您可以使用多种选项.

对于参数较少的构造函数,最简单的一个是.

var
 C : TRttiContext;
 O : TObject;
begin
  O := (C.FindType('UnitName.TClassName') as TRttiInstanceType).MetaClassType.Create;
  ...
 end;
Run Code Online (Sandbox Code Playgroud)

以下是使用.传递参数的示例 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;
Run Code Online (Sandbox Code Playgroud)

我在该单元上写了几篇文章,RTTI因为有很多选择.


根据David请求更新:

使用类型(虚拟构造函数)比较使用构造的用法 TRttiType.Invoke

类类型方法:(虚拟构造函数)

  • 适用于所有版本的Delphi
  • 生成更快的代码
  • 在编译时需要祖先的知识.
  • 需要类注册表以字符串名称查找类(如RRUZ所述)

TRttiType.Invoke()方法

  • 仅适用于Delphi 2010或更高版本.
  • 代码较慢
  • 实现一个将名称冲突考虑在内的类注册表
  • 需要NO在编译的时候祖先的知识.

我个人认为每个都有不同的用途.如果我知道前面的所有类型,我使用类类型方法.

  • 但是,请注意,你最好不要寻找一个名为Create的构造函数,因为Robert的代码只会调用`TObject.Create`的构造函数,如果`TClassName`的构造函数做了什么,那就错了去做.该实例将无法正确初始化. (2认同)

RRU*_*RUZ 16

您可以使用该GetClass函数,但必须先使用RegisterClassRegisterClasses方法注册类.

GetClass(const AClassName: string): TPersistentClass;
Run Code Online (Sandbox Code Playgroud)

  • 它为您提供了课程,因此您可以调用call create.`的getclass( '名称')创建;` (10认同)
  • @Johan:你*不能*在运行时创建一个类...如果你想挑剔,你可以创建类*实例*,而不是类.此外,无论您想使用哪种技术来获取类引用来创建实例,您都需要在类引用和您要用于检索它们的任何标识符之间进行某种映射.除非需要以非常通用的方式工作,否则恕我直言RTTI是使代码不可读(和慢速)的最佳方法.但是RTTI也将名称"注册"到相应的元数据和代码.无论如何,不​​要抱怨别人的答案,而是提供你自己的...... (8认同)
  • @Johan,我不明白你的评论和downvote,你读过有关`GetClass`函数的文档吗? (4认同)

Dav*_*nan 10

执行此操作的常规方法是使用虚拟构造函数.一个很好的例子是TComponent你无疑熟悉的.

TComponent 有以下构造函数:

constructor Create(AOwner: TComponent); virtual;
Run Code Online (Sandbox Code Playgroud)

另一个关键TComponentClass是声明为class of TComponent.

当VCL流式传输.dfm文件时,它会从.dfm文件中读取该类的名称,并且通过我们不需要在此处覆盖的某个过程,将该名称转换为变量,ComponentClass例如类型TComponentClass.然后它可以用以下实例化对象:

Component := ComponentClass.Create(Owner);
Run Code Online (Sandbox Code Playgroud)

这是拥有虚拟构造函数的一大优势,我鼓励您采用相同的方法.

如果必须使用字符串来标识类,那么您仍然需要提供一个查找例程来将字符串类名转换为类引用.如果方便的话,您可以挂钩到使用的相同VCL机制TComponent,即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;
Run Code Online (Sandbox Code Playgroud)