我遇到了在动态对象创建过程中调用错误的虚拟Create()方法的问题。调用父方法而不是后代方法。
我已经查看了这些帖子,但无法弄明白:
Delphi - Create class from a string
Exploring TRTTIType and Descendants
Can I pass a Class type as a procedure parameter
我有以下课程:
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中使用调试器逐步执行代码时,“评估/修改”工具会按预期告诉我ClassRef = TCellTrialAObj。
但是当我进入ClassRef.Create(self)行时,它会转到 TCellObj .Create(),而不是TCellTrialAObj.Create(),正如我所料。这是我不理解的部分。
将结果分配给CellObj后,评估/修改工具告诉我CellObj.ClassName ='TCellTrialAObj';
所以ClassRef是TCellTrialAObj,那么为什么没有Create()函数调用TCellTrialAObj.Create()??
提前致谢。
P.S。我使用的是Embarcadero®Delphi10 Seattle版本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;
答案 0 :(得分: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;