kdt*_*top 8 delphi oop polymorphism inheritance
我一直在收到关于Delphi编译器的警告 Method 'Create' hides virtual method of base.
我已经回顾了几个Stack Overflow链接(见下文),我不明白这个警告背后的逻辑,以及为什么它被认为是错误的编码实践.我希望别人能帮助我理解
我将包含一些示例代码:
type
TMachine = class(TPersistent)
private
public
Horsepower : integer;
procedure Assign(Source : TMachine);
end;
...
procedure TMachine.Assign(Source : TMachine);
begin
inherited Assign(Source);
Self.Horsepower := Source.HorsePower;
end;
Run Code Online (Sandbox Code Playgroud)
这会导致编译器警告.
[dcc32 Warning] Unit1.pas(21): W1010 Method 'Assign' hides virtual method of base type 'TPersistent'
Run Code Online (Sandbox Code Playgroud)
我一直忽视这个警告,因为它对我没有任何意义.但这让我以另一种方式遇到了麻烦(请参阅我在这里的另一篇文章:为什么Delphi在动态对象创建过程中会调用错误的构造函数?)所以我决定尝试更好地理解这一点.
我知道如果我使用保留字reintroduce,错误就会消失,但我已经看到它反复发布这是一个坏主意.正如Warren P在这里所写的那样(德尔福:方法'创造'隐藏基础的虚拟方法 - 但它就在那里),"恕我直言,如果你需要重新引入,你的代码闻起来很可怕".
我想我理解"隐藏"是什么意思.正如David Heffernan在这里所说的那样(导致"W1010方法'%s'隐藏基类型'%s'的虚拟方法"的原因是什么?):
隐藏的含义是,从派生类中,您不再能够访问基类中声明的虚方法.您不能引用它,因为它与派生类中声明的方法具有相同的名称.后一种方法是从派生类中可见的方法.
但我有点困惑,因为似乎祖先方法并没有真正隐藏,因为派生类总是只能使用inherited关键字来调用基类中的方法.所以'隐藏'真的意味着'有些隐藏'吗?
我想我也明白使用保留字override会阻止编译器警告,但程序签名必须相同(即没有新添加的参数).我不能在这里使用它.
我不明白为什么隐藏是值得警告的.在我上面的代码示例中,我不希望用户TMachine.Assign()以某种方式使用TPersistent.Assign().在我的扩展课程中,我扩展了需求,因此希望他们使用新的和改进的功能.因此,隐藏旧代码似乎正是我想要的.我对virtual方法的理解是基于运行时对象的实际类型调用正确方法的方法.在这种情况下,我认为不应该有任何影响.
附加代码,将添加到上面的示例代码中
TAutomobile = class(TMachine)
public
NumOfDoors : integer;
constructor Create(NumOfDoors, AHorsepower : integer);
end;
...
constructor TAutomobile.Create(ANumOfDoors, AHorsepower : integer);
begin
Inherited Create(AHorsepower);
NumOfDoors := ANumOfDoors;
end;
Run Code Online (Sandbox Code Playgroud)
这会添加新的编译器警告消息: [dcc32 Warning] Unit1.pas(27): W1010 Method 'Create' hides virtual method of base type 'TMachine'
我特别不理解使用带有附加参数的新构造函数时出现的问题.在这篇文章中(SerialForms.pas(17):W1010方法'创建'隐藏基类型'TComponent'的虚方法),智慧似乎应该是引入具有不同名称的构造函数,例如CreateWithSize.这似乎允许用户选择他们想要使用的构造函数.
如果他们选择旧的构造函数,扩展类可能会缺少一些创建所需的信息.但是,如果相反,我"隐藏"了先前的构造函数,那就是编程错误.Marjan Venema reintroduce在同一链接中写道:重新引入断开多态性.这意味着您不能再使用元类(TxxxClass = Tyyy类)来实例化您的TComponent后代,因为它的Create将不会被调用.我根本不明白这一点.
也许我需要更好地理解多态性.Tony Stark在这个链接中写道(什么是多态,它是什么,以及它是如何使用的?)多态性是:"面向对象编程的概念.不同对象以自己的方式响应的能力,相同的消息称为多态." 那么我是否提出了一个不同的接口,即不再是相同的消息,从而打破了多态性?
我错过了什么?总之,在我的例子中,不是隐藏基码是一件好事吗?
这里的危险是您可能会调用Assign基类引用.因为您没有使用,override所以不会调用派生类方法.你已经破坏了多态性.
根据最小惊喜的原则,您应该override在这里使用,或者为您的派生类方法赋予不同的名称.后一种选择很简单.前者看起来像这样:
type
TMachine = class(TPersistent)
public
Horsepower : integer;
procedure Assign(Source : TPersistent); override;
end;
...
procedure TMachine.Assign(Source : TPersistent);
begin
if Source is TMachine then begin
Horsepower := TMachine(Source).Horsepower;
end else begin
inherited Assign(Source);
end;
end;
Run Code Online (Sandbox Code Playgroud)
这允许您的班级与多态设计合作TPersistent.没有使用override那是不可能的.
您的下一个示例与虚拟构造函数类似.使构造函数成为虚拟的整个过程,以便您可以在运行时直到创建实例而不知道它们的类型.规范示例是流式框架,即处理.dfm/.fmx文件并创建对象并设置其属性的框架.
流式传输框架依赖于以下虚拟构造函数TComponent:
constructor Create(AOwner: TComponent); virtual;
Run Code Online (Sandbox Code Playgroud)
如果您希望组件使用流式框架,则必须覆盖此构造函数.如果你隐藏它,那么流式框架找不到你的构造函数.
考虑流式传输框架如何实例化组件.它不知道它需要使用的所有组件类.例如,它不能考虑第三方代码,即您编写的代码.Delphi RTL无法知道那里定义的类型.流式框架实例化这样的组件:
type
TComponentClass = class of TComponent;
var
ClassName: string;
ClassType: TComponentClass;
NewComponent: TComponent;
....
ClassName := ...; // read class name from .dfm/.fmx file
ClassType := GetClass(ClassName); // a reference to the class to be instantiated
NewComponent := ClassType.Create(...); // instantiate the component
Run Code Online (Sandbox Code Playgroud)
该ClassType变量包含一个元类.这允许我们表示直到运行时才知道的类型.我们需要以Create多态方式调度调用,以便执行组件构造函数中的代码.除非你override在声明构造函数时使用它,否则它不会.
实际上,所有这些归结为多态性.如果您对多态性的理解不如您所建议的那么坚定,那么您将很难理解任何这一点.我认为你的下一步行动是更好地掌握多态性.
| 归档时间: |
|
| 查看次数: |
916 次 |
| 最近记录: |