Delphi中reintroduce和override指令的含义是什么?
override
和 reintroduce
指令之间有什么区别? 什么时候不应该在重写方法中使用 inherited
关键字?
What is the difference between the override
and reintroduce
directives? And when should I not use the inherited
keyword in overridden methods?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
顺便说一句,对吉姆答案的引用非常好,仅描述了何时何地使用指令。 答案的另一部分是为什么仍然需要它们? 许多语言没有它们也能相处得很好,对吧? 在设计 Delphi Object Pascal 语言的各个方面时,OOP(面向对象编程)多年来一直是主流。 在此期间,人们发现使用许多采用这些概念的语言(Turbo Pascal、C++ 等)来开发应用程序框架都遇到了我所说的“版本 2”问题。
假设您使用 X 语言开发了一个很棒的框架并将其发布为版本 1。您的用户对它的所有功能赞不绝口,并且它被大量使用。 您欣喜若狂,决定发布更精彩的版本 2。 您特别确保它完全向后兼容。 突然,你们的用户开始报告奇怪的行为。 他们自己的虚拟方法在奇怪的时间被调用。 许多人报告说他们的旧代码无法在新版本中编译。 奇怪的。 所有相同的对象、方法和功能仍然保留。 您所做的只是向一些基类、一些新的对象类型和一些新的可选功能添加一些虚拟方法。 发生了什么?
override 和 reintroduce 指令用于消除此问题,因为要求为了实际覆盖虚拟方法,您必须使用 override 指令来代替虚拟指令。 如果您碰巧引入了自己的虚拟方法,该方法与祖先的虚拟方法之一同名,编译器现在会警告您,但仍然会做正确的事情。 在这种情况下,使用重新引入不仅可以抑制该警告,还可以在您打算执行此操作的源中记录下来。
如果没有覆盖和重新引入指令,您将无法不断发展您的框架而不担心破坏所有用户。 如果每次发布新版本时您的用户都必须进行大量修改,那么他们将不愿意采用新版本。 最后,使用“覆盖”还允许框架设计者在不破坏用户代码的情况下更改祖先中的虚拟类型。 例如,在 Delphi 中,许多方法都被标记为“动态”,这是一种基于表的多态性运行时方法查找形式。 它的执行速度不如常规虚拟机那么快,因此它通常用于很少被覆盖的方法和/或响应用户操作而从未注意到额外的开销。 假设在框架的 V1 中,一个方法被标记为“动态”,但实际上它最终被覆盖并调用了超出您预期的次数。 在 V2 中,您可以将其更改为“虚拟”,而不必担心用户的代码被破坏。
Delphi 的 Object Pascal 语言并不是唯一认识到这个问题的语言。 出于完全相同的原因,C# 需要使用“覆盖”指令。 C++ 标准委员会终于认识到了这个问题,并正在修改语言以支持它......有点。 在 C++ 中,如果方法的名称和参数列表与祖先的虚拟方法相匹配,那么它就是一个覆盖(即使您没有在后代上说“虚拟”!)。 对于即将推出的新 C++ 标准,如果您指定“virtual”并且签名不匹配,那么它是当前类中引入的新虚拟方法。 如果签名与祖先匹配并且编写者不打算覆盖,则使用“new”关键字告诉编译器这是一个new这门课的虚拟。
The reference to Jim's answer, which was excellent BTW, only described the when and where to use the directives. Another part of the answer is why are they needed anyway? Many languages get along just fine without them, right? When designing aspects of the Delphi Object Pascal Language, OOP (Object Oriented Programming) had been in the mainstream for several years. During this time it was observed that using many of the languages that had adopted those concepts (Turbo Pascal, C++, etc..) to develop application frameworks suffered from what I called the "version 2" problem.
Suppose you developed an awesome framework using language X and released it as version 1. Your users raved at all it could do and it became heavily used. Flush with success, you decide to release version 2 with even more awesomeness. You specifically made sure it was fully backward compatible. Suddenly, you users started reporting strange behaviors. Their own virtual methods were being called at strange times. Many reported that their old code would not compile with the new version. Strange. All the same objects, methods, and functionality still remained. All you did was add a few virtual methods to some base classes, some new object types, and some new optional functionality. What happened?
The override and reintroduce directives serve to eliminate this problem by requiring that in order to actually override a virtual method you must use the override directive in place of the virtual directive. If you happen to introduce your own virtual method that has the same name as one of your ancestors' virtual methods, the compiler now warns you, but will still do the right thing. In this case, using reintroduce, not only suppresses that warning, it also serves to document in the source you intended to do that.
Without the override and reintroduce directives, you would not be able to continually evolve your framework without fear of breaking all your users. And if your users had to make massive modifications every time a new version is released, then they would be loathe to adopt the new version. Finally, using "override" also allows the framework designer to change the type of virtual in the ancestors without breaking user code. For instance, in Delphi many methods are marked "dynamic" which is a table-based runtime method lookup form of polymorphism. It's doesn't perform quite as fast as a regular virtual so it is usually used for methods that are rarely overridden and/or are responses to user actions where the extra overhead is never noticed. Suppose in V1 of the framework a method was marked "dynamic" but in practice it ended up being overridden and called more than you intended. In V2, you could change it to "virtual" without fear of user's code being broken.
Delphi's Object Pascal language isn't the only language to recognize this problem. C# requires the use of an "override" directive for the exact same reason. The C++ standards committee is finally recognizing the problem and are modifying the language to support it... sort of. In C++, if a method's name and parameter list matches an ancestor's virtual, then it is an override (even if you don't say "virtual" on the descendant!). For the upcoming new C++ standard, if you specify "virtual" and the signatures don't match then it is a new virtual method introduced on current class. If there is a signature match with the ancestor and the writer didn't intend to override, then the "new" keyword is used to tell the compiler that this is a new virtual for this class.
override指令用于重写继承类中的虚拟方法。
reintroduce 指令用于声明与超类中名称相同但参数不同的方法。
The override directive is used to override virtual methods in inherited classes.
The reintroduce directive is used to declare a method with the same name as in the super class, but with different parameters.
基本上,答案是当您不希望执行继承的方法时。 但要小心,因为不允许运行继承的方法可能会以不良的方式破坏继承对象的功能,因此请确保不会引入任何意外的副作用。
举个例子,假设您希望完全重写名为 ApplyDiscount 的继承函数,但有人已将折扣百分比硬编码到祖先类中。 如果您调用继承的ApplyDiscount,它将覆盖您的代码或计算一个您将覆盖的值; 在这种情况下,您不能调用继承并自己应用折扣。
(这是一个人为的示例,因此如果有人能想到更好的示例,请添加它。)
Basically, the answer is when you don't wish the inherited method to execute. Be careful though as not allowing an inherited method to run may break functionality of an inherited object in a undesirable way, so make sure you are not introducing any unintended side effects.
As an example, imagine you wish to fully override an inherited function called ApplyDiscount, but someone has hardcoded the discount percentage into the ancestor class. If you call the inherited ApplyDiscount it will override your code or calculate a value you will then override; in this case you could just not call inherited and apply the discount yourself.
(This is a contrived example so if someone can think of a better one please add it.)
在很多情况下,您不想在重写的方法中调用继承。
在我使用的某些库中,基本方法会引发错误(ENotImplemented 或类似错误)。 显然,在这种情况下,您不想调用继承,否则您的代码也会引发错误。
我的一些类有一个适用于大多数情况的默认实现。 该方法只是重写来替换默认的,不需要调用默认的。
例如,我的基础财务对象的 GST 函数(= 销售税)返回 ExGst 金额的 12.5%,IncGst 返回 ExGst + GST。
在我的收入补偿对象上,GST 始终返回 0,因此无需调用继承的函数。
There are many circumstances where you don't want to call inherited in an overridden method.
In some libraries that I use, the base method throws an error (ENotImplimented or similar). Obviously in that case you don't want to call inherited or your code would also throw the error.
Some of my classes have a default implementation that works in the majority of cases. The method is only overridden to replace the default, and there is no need to call the default.
For example, the GST function (= sales tax) on my base financial object returns 12.5% of the ExGst amount and IncGst returns ExGst + GST.
On my Income Compensation objects, GST always returns 0, so there is no need to call the inherited function.