Why does Delphi call the wrong constructor when creating a dynamic object?

I am having problems calling the Create () virtual method incorrectly while creating a dynamic object. The parent method is called, not the decendant method.

I looked through these posts but cannot figure it out:
Delphi - create a class from a string

Learning TRTTIType and Descendants

Can I pass a class type as a procedure parameter

and here Class_References

I have the following classes:

TCellObj = class(TPhysicsObj) ... public constructor Create(RgnMgr : TObject); virtual; //RgnMgr should be TRgnManager destructor Destroy; ... end; TCellObjClass = Class of TCellObj; -------------------------------- TCellTrialAObj = class(TCellObj) ... public ... constructor Create(RgnMgr : TObject); virtual; //RgnMgr should be TRgnManager end; -------------------------------- TRgnManager = class (TObject) ... public function NewCell(ClassRef : TCellObjClass) : TCellObj; ... end; .... function TRgnManager.NewCell(ClassRef : TCellObjClass) : TCellObj; var CellObj : TCellObj; begin CellObj := ClassRef.Create(Self); CellObj.DefaultInitialize; CellObj.Color := TAlphaColors.Slategray; FCellsList.Add(CellObj); //This will own objects. SetSelection(CellObj); Result := CellObj; end; 

And finally, I begin the process of creating a dynamic object on the following line:

 RgnManager.NewCell(TCellTrialAObj); 

My goal is for TRgnManager.NewCell to create any decendant from TCellObj based on the derived class passed as a parameter. I will be wrapping the result to the appropriate class type during use.

When I view the code using the debugger in NewCell, the Evaluate / Modify tool tells me that ClassRef = TCellTrialAObj, as expected.

But when I enter the ClassRef.Create (self) line, it goes to TCellObj .Create (), and not to TCellTrialAObj.Create (), as I expected. This is the part that I do not understand.

After the result has been assigned to CellObj, the Evaluate / Modify tool tells me that CellObj.ClassName = 'TCellTrialAObj';

So, ClassRef was from TCellTrialAObj, then why the Create () function did not call TCellTrialAObj.Create () ??

Thanks in advance.

PS I am using Embarcadero® Delphi 10 Seattle Version 23.0.22248.5795

ADDITION

I combined this function below using examples from the links above. It seems to work, and calls TCellTrialAObj.Create as desired. But I don’t understand how, why, or if I really do it right. Can someone explain?

  function TRgnManager.NewCell(ClassRef : TCellObjClass) : TCellObj; var CellObj : TCellObj; RT : TRttiType; C : TRttiContext; T : TRttiInstanceType; V : TValue; begin C := TRttiContext.Create; T := (C.GetType(ClassRef) as TRttiInstanceType); V := T.GetMethod('Create').Invoke(T.metaClassType,[self]); C.Free; CellObj := V.AsObject as TCellObj; //CellObj := ClassRef.Create(Self); CellObj.DefaultInitialize; CellObj.Color := TAlphaColors.Slategray; FCellsList.Add(CellObj); //This will own objects. SetSelection(CellObj); Result := CellObj; end; 
+2
dynamic delphi
source share
1 answer

Compiler warnings will help you here. You will probably notice when compiling that you will get a Method 'Create' hides virtual method of base type 'TCellObj' . This is because you declared the descendant constructor of TCellTrialAObj as virtual if you want it to be override instead.

Here a minimal example demonstrates the desired functionality.

 program Project1; {$APPTYPE CONSOLE} type TCellObj = class public constructor Create; virtual; end; TCellObjClass = Class of TCellObj; TCellTrialAObj = class(TCellObj) public constructor Create; override; end; constructor TCellObj.Create; begin WriteLn('TCellObj'); end; constructor TCellTrialAObj.Create; begin WriteLn('Calling base constructor...'); inherited; WriteLn('...and now in TCellTrialAObj constructor'); end; function NewCell(ClassRef : TCellObjClass) : TCellObj; var CellObj : TCellObj; begin CellObj := ClassRef.Create;; Result := CellObj; end; var LCellObj : TCellObj; begin LCellObj := NewCell(TCellTrialAObj); ReadLn; end. 

Aside, here you are using a comment to suggest a type restriction:

  constructor Create(RgnMgr : TObject); virtual; //RgnMgr should be TRgnManager 

It is possible, however, to declare the TRgnMgr class TRgnMgr and fully define it later, which allows you to include a much more robust formal type restriction.

  TRgnMgr = class; { Declare type... } TCellObj = class public constructor Create(RgnMgr : TRgnMgr); virtual; end; TCellObjClass = Class of TCellObj; TCellTrialAObj = class(TCellObj) public constructor Create(RgnMgr : TRgnMgr); override; end; TRgnMgr = class { but define it later } private FFoo : integer; end; 
+4
source share

All Articles