您只需要在base.SomeVirtualMethod
该 API 的文档指定您应该这样做时调用。否则,它应该被暗示为可选的。要求您调用基本方法但没有明确说明的 API 设计得很糟糕。
需要基本调用的原因是糟糕的设计,因为您永远无法期望覆盖您的方法的人会做什么,并且您不能确定他们会调用基本方法来执行任何必需或关键代码。
所以简而言之,请参阅文档,否则通常没有必要。.NET Framework 是按照这样的准则设计的,由于这些原因,大多数虚拟方法不需要调用基。那些做的记录在案。
感谢 roken 指出调用基本虚拟方法的一个非常重要的原因是在使用事件时。但是,我认为情况并非总是如此的反驳仍然适用,特别是如果您使用不遵循 .NET 习惯用法和模式的第三方库或类,则无法确定。举这个例子。
namespace ConsoleApplication12
{
using System;
using System.Diagnostics;
class Foo
{
public Foo() {
}
public event EventHandler Load;
protected virtual void OnLoad() {
EventHandler handler = Load;
if (handler != null) {
handler(this, new EventArgs());
}
Debug.WriteLine("Invoked Foo.OnLoad");
}
public void Run() {
OnLoad();
}
}
class DerivedFoo : Foo
{
protected override void OnLoad() {
base.OnLoad();
Debug.WriteLine("Invoked DerivedFoo.OnLoad");
}
}
class Program
{
static void Main(string[] args) {
DerivedFoo dFoo = new DerivedFoo();
dFoo.Load += (sender, e) => {
Debug.WriteLine("Invoked dFoo.Load subscription");
};
dFoo.Run();
}
}
}
Foo.OnLoad
如果您运行此示例,您将获得对、DerivedFoo.OnLoad
和事件订阅的三个调用dFoo.Load
。如果您注释掉对base.OnLoad
in的调用DerivedFoo
,您现在只会获得一次对 的调用DerivedFoo.OnLoad
,并且不会调用 base 和订阅者。
这一点仍然很重要,它取决于文档。仍然不能确定基本虚拟方法实现是否调用其订阅者。所以这应该很清楚。幸运的是,多亏了框架设计者,.NET 框架与 .NET 事件模型非常一致,但我仍然无法强调总是阅读 API 的文档。
当您根本不处理事件而是处理抽象基类之类的事情时,它会发挥很大作用。您如何知道是否为抽象类调用基事件?抽象类是否提供默认实现,还是希望您提供它?
文档是为虚拟会员定义合同的最有力、最清晰的方式。这就是 .NET 框架设计团队通常为交付的抽象类提供至少一个具体实现的原因之一。
我认为 Krzysztof Cwalina 在框架设计指南中说得最好。
我得到的一个常见问题是虚拟成员的文档是否应该说覆盖必须调用基本实现。答案是覆盖应该保留基类的契约。他们可以通过调用基本实现或其他方式来实现。很少有成员可以声称保留其合同(在覆盖中)的唯一方法是调用它。在很多情况下,调用 base 可能是保留合约的最简单方法(文档应该指出这一点),但很少绝对需要。
我完全同意。如果您覆盖基本实现并决定不调用它,您应该提供相同的功能。
我希望这能消除我在评论中的一些困惑。