C# - when to call base.OnSomething?

2019-05-31 22:51发布

I'm using Windows.Forms and have to inherit some controls to provide custom behavior. This inheritance obviously leads to method overriding.

So, here is the question - in which cases the order of calling base.OnSomething(...) can really affect the visible behavior of your program?

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

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

    base.OnRetrieveVirtualItem(e); // Is this always "correct"?
}

As far as I know for know, this order can be important when overriding paint-related methods (OnDrawItem, ...), but I guess there are some other ways to shoot yourself in the leg, because Windows.Forms does a lot of unmanaged code calls with possible side effects.

So, when could it possibly matter? And what are the rules of thumb about choosing the correct place to call the base method in these cases?

4条回答
Explosion°爆炸
2楼-- · 2019-05-31 23:23

You only need to call the base.SomeVirtualMethod when the documentation for that API specifies you should do so. Otherwise, it should be implied as optional. API's that require you to call the base method, but do not explicitly state so, are badly designed.

The reason requiring a base call is poor design, is because you can never expect what someone overriding your method will do, and you cannot be certain they will call the base method to execute any required or critical code.

So in short, refer to the documentation, otherwise no it is usually not necessary. The .NET Framework was designed by such guidelines, most virtual methods don't require a call to the base for these reasons. The ones that do are documented.

Thanks to roken who pointed out a very important reason to call base virtual methods, is when using events. However, my counter argument that this is not always the case still applies, especially if you are using third party libraries or classes that don't follow the .NET idiom and patterns, there just isn't any certainty. Take this example.

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();
        }
    }
}

If you run this example, you will get three invocations to Foo.OnLoad, DerivedFoo.OnLoad, and the event subscription dFoo.Load. If you comment out the call to base.OnLoad in DerivedFoo, you will now only get a single invocation to DerivedFoo.OnLoad, and the base and subscriber did not get called.

The point is still strong that its up to documentation. There is still no certainty that a base virtual method implementation invokes its subscribers. So that should be clear. Luckily the .NET Framework is extremely consistant to the .NET event model thanks to the framework designers, but I still cannot stress enough to always read the documentation for the API.

It comes into play a lot when you're not dealing with events at all, but things like abstract base classes. How do you know whether to call a base event for an abstract class? Does the abstract class provide a default implementation, or does it expect you to provide it?

Documentation is the strongest, clearest way to define a contract for a virtual member. This is one reason why the .NET framework designer team generally provides at least a single concrete implementation for an abstract class that gets shipped.

I think that Krzysztof Cwalina says it best in the framework design guidelines.

A common question I get is whether documentation for virtual members should say that the overrides must call the base implementation. The answer is that overrides should preserve the contract of the base class. They can do it by calling the base implementation or by some other means. It is rare that a member can claim that the only way to preserve its contract (in the override) is to call it. In a lot of cases, calling the base might be the easiest way to preserve the contract (and docs should point that out), but it's rarely absolutely required.

And I agree completely. In the case you override a base implementation and decide not to call it, you should provide the same functionality.

I hope this clears up some confusion I had in the comments.

查看更多
祖国的老花朵
3楼-- · 2019-05-31 23:31

In general, you'd better to call the base method first : it configures a class. Then you run your own logic.

For example: When you override OnSelectedItemChanged - you call base method, it switches you class to the right condition, and then you can do what you want (do something whith a new selected item).

So, it will be useful to know what's going on in base method. Maybe you don't need to call it.

How to choose: just check the class in DotPeek and see if you really need to call base method.

When it matter: The base method can override your changes. And you'll get strange behaviour.

查看更多
ゆ 、 Hurt°
4楼-- · 2019-05-31 23:34

One special case to consider: If you are using the Dispose(bool) idiom, you MUST call base.Dispose(bool) AFTER you have cleaned up your own resources.

(This is relevant to Windows.Forms, I think, because they use the Dispose(bool) idiom)

查看更多
在下西门庆
5楼-- · 2019-05-31 23:41

As a "rule of thumb" in WinForms, with the On[EventName] (ie, OnFormClosing) methods you must call the base method in order for the corresponding event to be fired by the framework class (else the event will not be raised by the control). Bad design or not, that is a very common pattern.

查看更多
登录 后发表回答