C# 什么时候给base.on打电话?

C# 什么时候给base.on打电话?,c#,winforms,winapi,overriding,base,C#,Winforms,Winapi,Overriding,Base,我使用的是Windows.Forms,必须继承一些控件以提供自定义行为。这种继承显然会导致方法重写 因此,这里有一个问题-在什么情况下调用base.OnSomething(…)的顺序会真正影响程序的可见行为 protected override OnRetrieveVirtualItem(RetrieveVirtualItemEventArgs e) { // base.OnRetrieveVirtualItem(e); - Could this potentially break so

我使用的是
Windows.Forms
,必须继承一些控件以提供自定义行为。这种继承显然会导致方法重写

因此,这里有一个问题-在什么情况下调用
base.OnSomething(…)
的顺序会真正影响程序的可见行为

protected override OnRetrieveVirtualItem(RetrieveVirtualItemEventArgs e)
{
    // base.OnRetrieveVirtualItem(e); - Could this potentially break something?

    // Perform your custom method.
    // ...

    base.OnRetrieveVirtualItem(e); // Is this always "correct"?
}
据我所知,在重写与绘制相关的方法(
OnDrawItem,
)时,这个顺序可能很重要,但我想还有其他一些方法可以打击你自己,因为
Windows.Forms
会执行大量非托管代码调用,可能会产生副作用


那么,什么时候可能会有问题呢?在这些情况下,选择调用
base
方法的正确位置的经验法则是什么?

一般来说,最好先调用base方法:它配置类。然后你运行你自己的逻辑

例如:当您重写
OnSelectedItemChanged
-您调用base方法时,它会将您的类切换到正确的条件,然后您可以做您想做的事情(使用新的选定项做某事)

所以,了解基本方法中发生了什么是非常有用的。也许你不需要叫它

如何选择:只需检查DotPeek中的类,看看是否真的需要调用base方法


重要时:基本方法可以覆盖您的更改。你会有奇怪的行为。

当API的文档指定你应该这样做时,你只需要调用
base.SomeVirtualMethod
。否则,它应该暗示为可选。要求您调用基方法但没有显式声明的API设计得很糟糕

需要基调用的原因是糟糕的设计,因为您永远无法预料重写您的方法的人会做什么,并且您无法确定他们会调用基方法来执行任何必需的或关键的代码

因此,简而言之,请参阅文档,否则通常不需要。NET框架就是根据这样的指导原则设计的,由于这些原因,大多数虚拟方法不需要对基函数进行调用。有记录的

感谢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
的三个调用。如果您在
DerivedFoo
中注释掉对
base.OnLoad
的调用,您现在只会得到对
DerivedFoo.OnLoad
的一次调用,并且基本用户和订户没有被调用

这一点仍然很重要,它取决于文档。基本虚拟方法实现是否调用其订阅者仍然不确定。所以这应该是清楚的。幸运的是,由于框架设计人员的帮助,.NET框架与.NET事件模型非常一致,但我仍然无法强调始终阅读API的文档

当您根本不处理事件,而是处理抽象基类之类的事情时,它会发挥很大作用。如何知道是否为抽象类调用基事件?抽象类是提供默认实现,还是希望您提供它

文档是为虚拟成员定义契约的最强大、最清晰的方法。这就是为什么.NET framework designer团队通常为发布的抽象类提供至少一个具体实现的原因之一

我认为Krzysztof Cwalina在框架设计指南中说得最好

我遇到的一个常见问题是,虚拟成员的文档是否应该说重写必须调用基本实现。答案是重写应该保留基类的约定。他们可以通过调用基本实现或通过其他方式来实现。很少有成员会声称维护其契约(在重写中)的唯一方法是调用它。在很多情况下,调用base可能是保存合同的最简单方法(文档应该指出这一点),但很少绝对需要这样做

我完全同意。如果您重写了一个基本实现并决定不调用它,那么您应该提供相同的功能


我希望这能消除我在评论中的一些困惑。

需要考虑的一个特殊情况是:如果使用Dispose(bool)习惯用法,则必须在清理自己的资源后调用base.Dispose(bool)


(我认为这与Windows.Forms有关,因为它们使用Dispose(bool)习惯用法)

作为WinForms中的“经验法则”,使用On[EventName](即OnFormClosing)方法时,必须调用base方法,以便框架类触发相应的事件(否则该事件将不会由控件引发)。无论设计是否糟糕,这是一种非常常见的模式。

Ok:)我会给她你的匿名“女贞”。@DavidAnderson,我不会说roken对Windows窗体中存在的每一个[EventName]都是正确的(可能有一个是一般规则的例外)。然而,一般情况就是他描述的情况。例如,来自MSDN:“当在派生类中重写OnMouseUp时,请确保调用基类的OnMouseUp方法,以便注册的委托接收事件。”@DavidAnderson关于未引发事件的部分完全正确,为什么说不是?OnLoad的基本实现(例如)是引发事件的地方,如果重写virual方法并