Recently I found a piece of code that creates an instance of TButton from a string: 'TButton' was used as a parameter.
See "Is there a way to instantiate a class by its name in Delphi?"
I am trying to save published properties of any object to an XML file (which works fine), and lately I want to recreate these objects from the XML file. In this file is written which class is supposed to be created (for example TButton) and then follows a list of properties, which should be loaded into this run-time-created object.
The example above shows the way how to do it, but it does not work for the class of my own. See code below:
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;
I can not get through. The TTripple object is perhaps created, but its constructor is not used.
The TRipple constructor isn't being called because it's not virtual.
When you're constructing an object from a class reference, the compiler doesn't know what the final class type is yet, so it can't assign the right constructor in code. All it knows is that it's descending from TPersistent, so it writes out code to call the constructor for TPersistent, which is TObject.Create. If you want to call the right constructor, you have to do it virtually.
There's already a virtual constructor defined for reading classes from a class name. It's defined in TComponent. Make TRipple descend from TComponent and override its virtual constructor (the one that takes an Owner as a parameter) and then your code will work.
You may not want to use TComponent, and there is another way of doing this.
add a reference to your class
TTrippleClass = class of TTripple;
Then your buttonclick becomes :
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;
Now you may want to have more than one Tripple type then create an custom ancestor.
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;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With