Reputation: 2619
Here is the object:
TCell = class(TPersistent)
private
FAlignmentInCell :byte;
public
constructor Create; virtual;
published
property AlignmentInCell:byte read FAlignmentInCell write FAlignmentInCell;
end;
this is its constructor:
constructor TCell.Create;
begin
inherited;
FAlignmentInCell:=5;
end;
Here is a function, which dynamically creates any object derived form TPersistent (parameter is class name provided as a string)
function CreateObjectFromClassName(AClassName:string):TPersistent;
var DynamicObject:TPersistent;
TempObject:TPersistent;
DynamicPersistent:TPersistent;
DynamicComponent:TComponent;
PersistentClass:TPersistentclass;
ComponentClass:TComponentClass;
begin
PersistentClass:=TPersistentclass(FindClass(AClassName));
TempObject:=PersistentClass.Create;
if TempObject is TComponent then
begin
ComponentClass:=TComponentClass(FindClass(AClassName));
DynamicObject:=ComponentClass.Create(nil);
end;
if not (TempObject is TComponent) then
begin
DynamicObject:=PersistentClass.Create; // object is really TCell, but appropriate constructor seems to be not called.
end;
result:=DynamicObject;
end;
My idea is to create new Cell (TCell) like this:
procedure TForm1.btn1Click(Sender: TObject);
var p:TPersistent;
begin
p := CreateObjectFromClassName('TCell');
ShowMessage(IntToStr(TCell(p).AlignmentInCell)); // it is 0. (Why?)
end;
When I want to check AlignmentInCell property I get 0, but I expected 5. Why? Is there way to fix it?
Upvotes: 1
Views: 825
Reputation: 1328
The compiler can't know for sure what value your variable of type TPersistentClass
will hold at run time. So he assumes that it is exactly that: a TPersistentClass
.
TPersistentClass
is defined as a class of TPersistent
. TPersistent
has no virtual constructor, the compiler will therefore not include a call to dynamically look up the address of the constructor in the VMT of the actual class, but a 'hard-coded' call to the only matching constructor TPersistent
has: the one it inherits from its base class TObject.
It might be a decision with reasons I don't know, but if you had chosen to define TCell
as following
TCell = class(TComponent)
private
FAlignmentInCell: byte;
public
constructor Create(AOwner: TComponent); override;
published
property AlignmentInCell:byte read FAlignmentInCell write FAlignmentInCell;
end;
you wouldn't need TempObject
and all the decision making in your CreateObjectFromClassName
function (and the possible leaks as pointed out by others):
function CreateObjectFromClassName(AClassName:string): TComponent;
var
ComponentClass:TComponentClass;
begin
ComponentClass:=TComponentClass(FindClass(AClassName));
Result := ComponentClass.Create(nil);
end;
And make sure to manage the Result
s life-time as it has no Owner
.
Upvotes: 2
Reputation: 28806
This is similar to a recent question.
You use TPersistentClass
. But TPersistent
does not have a virtual constructor, so the normal constructor for TPersistent
is called, which is the constructor it inherits from TObject
.
If you want to call the virtual constructor, you will have to declare a
type
TCellClass = class of TCell;
Now you can modify CreateObjectFromClassName
to use this metaclass instead of TPersistenClass
, and then the actual constructor will be called.
Also, TempObject
is never freed. And instead of is
, I would rather use InheritsFrom
.
I did not test the following, but it should work:
function CreateObjectFromClassName(const AClassName: string; AOwner: TComponent): TPersistent;
var
PersistentClass: TPersistentclass;
begin
PersistentClass := FindClass(AClassName);
if PersistentClass.InheritsFrom(TComponent) then
Result := TComponentClass(PersistentClass).Create(AOwner)
else if PersistentClass.InheritsFrom(TCell) then
Result := TCellClass(PersistentClass).Create
else
Result := PersistentClass.Create;
end;
Upvotes: 5