2009-12-08 97 views
4

最近我发现了一段从字符串创建TButton实例的代码:'TButton'被用作参数。如何从提供类名称的字符串创建实例?

"Is there a way to instantiate a class by its name in Delphi?"

我想任何对象的published属性保存到一个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对象可能已创建,但其构造函数未使用。

回答

5

TRipple构造函数没有被调用,因为它不是虚拟的。

当您从类引用构造对象时,编译器不知道最终的类类型是什么,所以它不能在代码中分配正确的构造函数。它只知道它是从TPersistent降序的,所以它写出了代码来调用TPersistent的构造函数,即TObject.Create。如果你想调用正确的构造函数,你必须做到这一点。

已经有一个虚拟构造函数定义为从类名读取类。它在TComponent中定义。让TRipple从TComponent下降并覆盖它的虚拟构造函数(将Owner作为参数的构造函数),然后您的代码将工作。

+0

非常感谢,梅森。它现在的作品....非凡... :-) – lyborko 2009-12-08 14:13:37

+0

很高兴能够帮助! – 2009-12-08 14:27:15

5

您可能不想使用TComponent,并且还有另一种方法来执行此操作。

添加引用您的类

TTrippleClass = class of TTripple; 

那么你buttonclick变为:

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; 
+0

您是否尝试过编译这个?由于TCustomTripple的双重声明(如'TPersistent'和'TCustomTripple类'),它将不起作用。 – 2009-12-08 13:42:26

+0

我做了,但后来复制它(手工),因为我在另一台机器上的Delphi到我发布答案的那台机器上。我会检查它。 – Steve 2009-12-08 14:05:11

+0

谢谢你,你的第一个例子工作正常。我很感激。在第二个我无法完成的事情 - 正如你所说的那样。对于我的puropose梅森的答案是足够的... – lyborko 2009-12-08 14:18:59

相关问题