如何通过提供类名的字符串创建一个实例?

5
最近我发现了一段代码,可以通过一个字符串创建TButton的实例:'TButton'被用作参数。
请参见"有没有办法在Delphi中按名称实例化类?" 我正在尝试将任何对象的已发布属性保存到XML文件中(这很好用),最近我想从XML文件中重新创建这些对象。在该文件中写入应该创建哪个类(例如TButton),然后是一个属性列表,应加载到此运行时创建的对象中。
上面的示例展示了如何执行此操作,但对于我自己的类不起作用。请参见下面的代码:
  TTripple=class (TPersistent)
    FFont:TFont;
  public
    constructor Create;
    Destructor Destroy;override;
  published
    property Font:TFont read FFont write  FFont;
  end;
var
  Form1: TForm1;


implementation

{$R *.dfm}

constructor TTripple.Create;
  begin
  inherited;
  FFont:=TFont.Create;
  end;


destructor TTripple.Destroy;
  begin
  FFont.Free;
  inherited;
  end;

procedure TForm1.FormCreate(Sender: TObject);
begin
RegisterClasses([TButton, TForm, TTripple]);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  CRef : TPersistentClass;
  APer : TPersistent;
begin
 // CRef := GetClass('TButton');
  CRef := GetClass('TTripple');
  if CRef<>nil then
  begin
     APer := TPersistent(TPersistentClass(CRef).Create);
     ShowMessage(APer.ClassName);  // shows TTripple, what is correct
     if APer is TTripple then (APer as TTripple).Font.Color:=90;

     /// Here  I get error message, because TTriple was not created... ?!?!?!

  end;
end;

我无法通过。TTripple对象可能已经被创建,但其构造函数未被使用。

2个回答

6
TRipple构造函数没有被调用,因为它不是虚函数。 当你从一个类引用中构造一个对象时,编译器还不知道最终的类类型,所以它无法在代码中分配正确的构造函数。它只知道它是从TPersistent继承而来的,因此它写出了调用TPersistent的构造函数的代码,也就是TObject.Create。如果你想调用正确的构造函数,你必须使用虚函数。 TComponent中已经定义了一个读取类名的虚构造函数。将TRipple从TComponent继承,并重写它的虚构造函数(带有Owner参数的那个),然后你的代码就可以工作了。

5

您可能不想使用TComponent,还有另一种方法可以实现此目的。

将您的类添加为引用即可。

TTrippleClass = class of TTripple;

然后你的按钮点击事件变成了:
procedure TForm1.Button1Click(Sender: TObject);
var
  CRef : TTrippleClass;
  APer : TPersistent;
begin
  CRef := TTrippleClass(GetClass('TTripple'));
  if CRef<>nil then
  begin
    APer := TTripple(TTrippleClass(CRef).Create);
    ShowMessage(APer.ClassName);  // shows TTripple, what is correct
    if APer is TTripple then (APer as TTripple).Font.Color:=90;
  end;
end;

如果您希望拥有多个Tripple类型,则可以创建一个自定义祖先。

TCustomTripple = class(TPersistent)
public
  constructor Create;virtual;
end;

TCustomTrippleClass = class of TCustomTripple;

TTripple = class(TCustomTripple)
strict private
  fFont : TFont;
public
  constructor Create;override;
  destructor Destroy;override;
  property Font : TFont read fFont;
end;


constructor TCustomTripple.Create;
begin
  inherited Create;
end;

constructor TTripple.Create;
begin
  inherited;
  fFont := TFont.Create;
end;

destructor TTripple.Destroy;
begin
  fFont.Free;
  inherited;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  CRef : TCustomTrippleClass;
  APer : TCustomTripple;
begin
  CRef := TCustomTrippleClass(GetClass('TTripple'));
  if CRef<>nil then
  begin
    APer := TCustomTripple(TCustomTrippleClass(CRef).Create);
    ShowMessage(APer.ClassName);  // shows TTripple, what is correct
    if APer is TTripple then (APer as TTripple).Font.Color:=90;
  end;
end;

你尝试编译过这个代码吗?它不会工作,因为 TCustomTripple 被重复声明了(既被声明为 'TPersistent' 又被声明为 'TCustomTripple 的类')。 - Ken White
我已经完成了,但是因为Delphi在另一台机器上,所以我手动复制了它。我会检查一下。 - Steve
谢谢Ken,你的第一个例子很好用。我很感激。在第二个例子中,我无法编译 - 就像你说的一样。对于我的目的,Mason的答案已经足够了... - lyborko
在第一个示例中,展示构造函数不需要是虚函数,将获得+1分。 - Rob Kennedy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接