【问题标题】:How to pass class reference (metaclass) as a parameter in procedure?如何在过程中将类引用(元类)作为参数传递?
【发布时间】:2026-01-08 03:30:02
【问题描述】:

有两个对象:TFooTFoo2

还有一个类引用:TFooClass = class of TFoo;

两者都是TPersistent的后代。

他们有自己的构造函数:

type
  TFoo = class(TPersistent)
  private
    FC:Char;
  public
    constructor Create; virtual;
  published
    property C:Char read FC write FC;
  end;
    
  TFoo2 = class(TFoo)
  public
    constructor Create; override;
  end;

  TFooClass = class of TFoo;

...

constructor TFoo.Create;
begin
  inherited Create;
  C :=' 1';
end;
    
constructor TFoo2.Create;
begin
  inherited Create;
  C := '2';
end;

我想从一个字符串创建一个TFoo2 对象,这实际上是它的类名:'TFoo2'

这是程序,它工作正常:

procedure Conjure(AClassName:string);
var
  PClass : TPersistentClass;
  p :TPersistent;
begin
  PClass := TPersistentClass(FindClass(AClassName))
  p := TFooClass(PClass).Create;  // <-- here is called appropriate constructor  
end;

现在,我想要类似的对象,例如:TBobodoTBobodo2

当然还有一个类参考:TBobodoClass = class of TBobodo;

等等……

现在,如何将类引用作为参数传递给过程,以确保调用正确的构造函数?

procedure Conjure(AClassName:string; ACLSREF: ???? ); // <-- something like that 
var
  PClass : TPersistentClass;
  p :TPersistent;
begin
  PClass := TPersistentClass(FindClass(AClassName))
  p := ACLSREF(PClass).Create;  // <-- something like that  
end;

有可能吗?

【问题讨论】:

  • 使用虚拟构造函数从基类派生所有类,并且不需要传递类引用
  • 是的,但我想用这种方式创建“低层次”对象,如 TFont、TPen 等....只是想知道,是否有一些可爱的解决方案
  • 我想这取决于你是否想要一个有效的解决方案
  • @DavidHeffernan 我有例程,可以将 TPersistent 对象(和所有后代)保存并加载到 XML 中。唯一的弱点是一个过程,我必须显式使用类引用来创建类,这看起来很丑。
  • 也许您可以使用RegisterClass 之类的方法,即创建基类名称到元类引用的映射。

标签: delphi delphi-7


【解决方案1】:

在 Delphi 7 中没有办法做你想做的事。元类引用必须在调用站点的编译时显式,而不是在运行时处理。

在 Delphi 2009 及更高版本中,您可能 1能够使用Generics 做某事,例如:

1:我自己还没试过。

type
  TConjureHelper = class
  public
    class procedure Conjure<TClassType>(const AClassName: string);
  end;

class procedure TConjureHelper.Conjure<TClassType>(const AClassName: string);
var
  PClass : TPersistentClass;
  p : TPersistent;
begin
  PClass := TPersistentClass(FindClass(AClassName));
  p := TClassType(PClass).Create;
  ...
end;

...

TConjureHelper.Conjure<TFooClass>('TFoo2');
TConjureHelper.Conjure<TBobodoClass>('TBobodo2');
...

但 Delphi 7 肯定不支持泛型。

【讨论】:

  • 我没有 Delphi 9,但您的回答帮助我为我的问题寻找另一种解决方案。谢谢....
【解决方案2】:

我遇到了同样的问题,经过一番挣扎,我找到了一个非常简单的解决方案:元类正是为此目的而发明的! 在您的情况下,您可以将元类作为参数传递并直接使用它,而无需繁琐的查找类和类型转换。

type
  TFooClass = class of TFoo;

procedure Conjure(aFooClass : TFooClass); // <-- something like that 
var
  p :TPersistent;
begin
  p := aFooClass.Create;  // it will work!
end;

通过调用,您只需使用:

Conjure(TFoo); // <- for Foo class or
Conjure(TFoo2); // <- for Foo2 class and so on

【讨论】:

  • 我在 FPC 上测试了我的解决方案,因此我认为在 Delphi 7 上它也必须工作。如果您只是对如何从类名创建实例感兴趣,那么您可以关注此线程。上述使用虚拟构造函数的方法将在 IMO not 工作。 (在FPC上测试,编译器会为基类的构造函数生成代码,不管它是否被声明为virtual)
最近更新 更多