2017-05-24 28 views
2

我在创建动态对象时调用错误的虚拟Create()方法时遇到了问题。父方法被调用而不是decendant方法。为什么在动态对象创建期间Delphi调用不正确的构造函数?

我已经审查了这些帖子,但无法弄清楚:
Delphi - Create class from a string

Exploring TRTTIType and Descendants

Can I pass a Class type as a procedure parameter

这里Class_References

我有以下类别:

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; 

最后我通过以下行启动动态对象创建的过程:

RgnManager.NewCell(TCellTrialAObj); 

我的目标是有TRgnManager.NewCell创建基于传过来的参数派生类TCellObj的任何子孙。我将在使用过程中将结果转换为适当的类类型。

当我用NewCell中的调试器遍历代码时,Evaluate/Modify工具告诉我ClassRef = TCellTrialAObj与预期的一样。

但是,当我踏进ClassRef.Create(个体经营)线,它进入TCellObj .Create(),而不是TCellTrialAObj.Create(),因为我本来期望。这是我不明白的部分。

将结果分配给CellObj后,Evaluate/Modify工具告诉我CellObj.ClassName ='TCellTrialAObj';因此,ClassRef是TCellTrialAObj,那么Create()函数为什么不调用TCellTrialAObj.Create()?

在此先感谢。

P.S.我使用Embarcadero®德尔福10西雅图版本23.0.22248.5795

附录

我拼凑低于此功能,使用从上面的链接的例子。它似乎工作,并根据需要调用TCellTrialAObj.Create。但我不明白如何,为什么,或者如果我真的做得对。谁能解释一下?

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

你淹没在警告和提示中,或者你为什么错过编译器告诉你什么是错的? –

+1

析构函数也应该覆盖它。您可以在TRgnManager上使用前向声明,以便它可以用作参数。代码中的许多问题比您确定的单个问题还多。 –

+0

@StefanGlienke和DavidHeffernan感谢您的反馈。看到其他评论,因为我试图教育自己,并避免未来类似的问题。 – kdtop

回答

4

编译器警告将帮助你在这里。编译时您可能会注意到,您会收到警告Method 'Create' hides virtual method of base type 'TCellObj'。这是因为你宣称后代TCellTrialAObj的构造函数为virtual,而我们推断你希望它是override

这里有一个最小的例子演示你想要的功能。

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. 

顺便说一句,在这里,你正在使用的注释表明一个类型的限制:

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

这是可能的,但是,为了使TRgnMgr类的向前声明和更高版本完全定义它,让您可以包含更强大的正式类型限制。

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

谢谢@J ...那样做了。当你理解时,这么容易...... :-)比我原先提出的一个附录更疯狂的RTTI解决方案更直接。再次感谢。 – kdtop

+1

@kdtop不要看我对类型限制的编辑 - 我认为这也是有趣的。 –

+2

@kdtop在理解多态性的基础上,我希望您能吸取教训:“永远不要忽略编译器警告。”他们告诉你,至少有一点可疑,即使不是彻头彻尾的_wrong_。您可以_allways_修改您的代码,使其不发出警告。所以你应该理解并消除你看到的任何编译器警告。 –

相关问题