我已经以基本形式重新引入了表单构造函数,但是如果我以子代形式覆盖原始构造函数,则重新引入的构造函数将不再可见。
type
TfrmA = class(TForm)
private
FWndParent: HWnd;
public
constructor Create(AOwner: TComponent; const AWndParent: Hwnd); reintroduce; overload; virtual;
end;
constructor TfrmA.Create(AOwner: TComponent; const AWndParent: Hwnd);
begin
FWndParent := AWndParent;
inherited Create(AOwner);
end;
type
TfrmB = class(TfrmA)
private
public
end;
type
TfrmC = class(TfrmB)
private
public
constructor Create(AOwner: TComponent); override;
end;
constructor TfrmC.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
end;
创建时:
frmA := TfrmA.Create(nil, 0);
frmB := TfrmB.Create(nil, 0);
frmC := TfrmC.Create(nil, 0); // Compiler error
我的解决方法是覆盖重新引入的构造函数或声明原始构造函数重载,但我想了解这种行为的原因。
type
TfrmA = class(TForm)
private
FWndParent: HWnd;
public
constructor Create(AOwner: TComponent); overload; override;
constructor Create(AOwner: TComponent; const AWndParent: Hwnd); reintroduce; overload; virtual;
end;
type
TfrmC = class(TfrmB)
private
public
constructor Create(AOwner: TComponent; const AWndParent: Hwnd); override;
end;
答案 0 :(得分:7)
在初始代码中,您的Create构造函数会隐藏原始Create。这就是为什么编译器抱怨TfrmC重写一个它再也看不到的构造函数了。
您通常会收到有关此内容的编译器消息,但“重新引入”会抑制此情况。每当您需要“重新引入”来抑制编译器消息时,警报响应就会消失,因为它表明您正在打破多态性。并不意味着你不应该使用它,但你应该意识到其含义。
在这些情况下,如果我想保留覆盖原始构造函数的能力,我倾向于添加不同的构造函数,即
constructor CreateWithParent(AOwner: TComponent; const AWndParent: HWnd); virtual;
并在其实现中调用原始构造函数。它确实意味着我需要调用一个不同的构造函数,但是你已经需要传递另一个参数,这似乎不是一个糟糕的权衡。
编辑: 正如我在评论中提到的,编译器抱怨tfrmC.Create(nil,0);参数太多了。似乎tfrmC.Create(AOwner)隐藏了tfrmA.Create(AOwner,AWndParent);在回家的路上考虑它(交通堵塞似乎总是有优势),对此有一个解释。
1)除了允许引入具有相同名称的其他构造函数之外,tfrmA.Create上的重载没有直接用途。 2)TfrmC构造函数有效地隐藏了TfrmA构造函数,重新引入了由TfrmA构造函数隐藏的签名。实际上,这不是对原始构造函数的重写,而是重新引入重新引入的构造函数。 3)我觉得编译器应该发出关于这种隐藏的警告。它没有的原因可能是TfrmA构造函数的过载指令。当您删除它时,您会收到有关TfrmC.Create声明与前一声明不同的错误。 4)由于TfrmC隐藏了TfrmA构造函数,编译器正确地抱怨TFrmC.Create(nil,0)具有太多参数。
当只使用override指令将TfrmA构造函数的签名添加到TfrmC时,2)中描述的内容变得很明显。然后,IDE会立即对此构造函数进行换行。原因:两个具有相同名称的构造函数和缺少重载指令的TfrmC构造函数只有AOwner参数。
当您在tfrmC的实例化中注释掉第二个参数并且仅编码“inherited”时,也会变得清晰。而不是“继承的创建(AOwner);”在其实施中。编译器然后抱怨不兼容的类型。
后者可以通过使用“inherited Create(AOwner);”来解决。 (正如你所做的那样)或“继承Create(AOwner,0);”。虽然后者可以确保继续使用继承树,但前者会直接跳回到TForm1.Create。
使用稍微修改后的code版本,并且提供的TfrmC使用单个参数进行实例化,“继承的Create(AOwner)”的结果将是:
alt text http://www.bjmsoftware.com/delphistuff/overloading/InheritedCreate1Param.jpg
而“继承Create(AOwner,0);”的结果本来可以: alt text http://www.bjmsoftware.com/delphistuff/overloading/InheritedCreate2Params.jpg
正如我在评论中建议的那样,为TfrmC构造函数添加重载会覆盖原始的TForm1.Create,并允许两个具有相同名称的构造函数。这可以通过打开TFRMC_OVERLOAD和INSTANTIATE2条件定义在测试项目中说明。这用TfrmC.Create(nil,0)实例化TfrmC并导致:
alt text http://www.bjmsoftware.com/delphistuff/overloading/InheritedCreate2ParamsOverload.jpg
其中显示为TfrmC调用TfrmB.Create,因为TfrmB是第一个具有双参数构造函数实现的祖先。在使用TfrmC.Create(nil)实例化TfrmC时(将INSTANTIATE2条件定义关闭),得到的图像与第一个或第二个相同(取决于TWOPARAMS定义)。
答案 1 :(得分:5)
所有使用相同名称声明的方法都需要重载。范围是当前的类声明。
最简单的解决方案是再次使用overload
指令:
type
TfrmC = class(TfrmB)
private
public
constructor Create(AOwner: TComponent); overload; override;
end;
原因是因为TCustomForm
中的原始构造函数未被声明为重载。