How to create an instance from a string that provides a class name?

I recently 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 name in Delphi?

I am trying to save the published properties of any object in an XML file (which works fine), and recently I want to recreate these objects from an XML file. This file records which class should be created (for example, TButton), and then a list of properties that should be loaded into this object created at run time follows.

The above example shows a way to do this, but it does not work for my class. See the following code:

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’t get through. A TTripple object may have been created, but its constructor is not used.

+4
source share
2 answers

The TRipple constructor is not called because it is not virtual.

When you create an object from a class reference, the compiler does not know what the last type of the class is, so it cannot assign the correct constructor in the code. All he knows is that he goes down with TPersistent, so he writes out code to call the constructor for TPersistent, which is TObject.Create. If you want to call the correct constructor, you should do it practically.

A virtual constructor has already been created there to read classes from the class name. It is defined in TComponent. Make TRipple go down with TComponent and override its virtual constructor (the one that takes the owner as a parameter), and then your code will work.

+5
source

You may not want to use TComponent, and there is another way to do this.

add a link to your class

 TTrippleClass = class of TTripple; 

Then your button will look like this:

 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 can have more than one type of Tripple, and then create a 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; 
+5
source

All Articles