我在动态对象创建期间调用了不正确的虚拟 Create() 方法时遇到了问题。调用父方法而不是子方法。
我已经查看了这些帖子,但无法弄清楚:
Delphi - Create class from a string
Exploring TRTTIType and Descendants
Can I pass a Class type as a procedure parameter
我有以下类(class):
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()呢??
提前致谢。
附注我正在使用 Embarcadero® Delphi 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;
最佳答案
编译器警告将帮助您解决这个问题。您可能会注意到,在编译时收到警告Method 'Create' hides virtual method of base type 'TCellObj'
。这是因为您已将后代 TCellTrialAObj
的构造函数声明为 virtual
,而我们却推断您希望它覆盖
。
这里有一个最小的示例演示了您想要的功能。
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;
关于delphi - 为什么 Delphi 在动态对象创建期间调用错误的构造函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44165088/