Replacing Methods in Derived Classes

So the implementation for the ExtinguishFire method that we want on the FireChief looks like this:

public void ExtinguishFire()
{
    // Get our number one to put out the fire instead
    TellFirefighterToExtinguishFire(NumberOne);
}

What happens if we just add that function to our FireChief and compile and run?

Well, it compiles, but when we run it, it still says:

Harry is putting out the fire!

It seems to have completely ignored our new function!

Let’s go back and have a look at that compiler output again. You’ll see that although it built and ran, there’s a warning (you may have to rebuild to get it to appear again; Choose Rebuild Solution from the Build menu):

'FireChief.ExtinguishFire()' hides inherited member
 'Firefighter.ExtinguishFire()'. Use the new keyword if hiding was intended.

Note

It is a good idea to leave all your compiler warnings on and work until you are both error and warning free. That way, when something crops up unexpectedly like this, you can spot it easily, rather than burying it in a pile of stuff you’re habitually ignoring.

It is telling us that, rather than replacing the implementation on the base class, our method (with matching signature) is hiding it; and that if this is what we really meant to do, we should add the keyword new to the method.

OK, let’s do that:

public new void ExtinguishFire()
{
    // Get our number one to put out the fire instead
    TellFirefighterToExtinguishFire(NumberOne);
}

We typically add the new modifier between the accessibility modifier and the return value.

Compile and run again. You’ll notice that we’ve gotten rid of the warning, but the output hasn’t changed:

Harry is putting out the fire!

What’s going on?

This method-hiding approach is actually letting a single object provide different implementations for the ExtinguishFire method. The implementation we get is based on the type of the variable we use, rather than the type of object to which the variable refers. You can see that happening if we use the code in Example 4-6 in our client.

The output we get now looks like this:

Joe is putting out the fire!
Harry is putting out the fire!

When we talk to our Harry object through a FireChief reference, he gets Joe to put out the fire. If we talk to the object through a Firefighter reference, he does it himself. Same object, but two completely different implementations.

Why might we want to do that?

Let’s say we had multiple fire chiefs on a job, but it is our policy that a chief acting as another chief’s Number One is not allowed to delegate the job again. Our code models exactly this behavior, as shown in Example 4-7.

Note

Of course, whether that’s desirable behavior is another matter entirely—we’ve ended up with such radically different approaches to putting out a fire that it might be better to separate them back out into functions with different names.

When you go through a refactoring process such as this, it is a good idea to check that you’re still happy with the semantic implications of your code. Ideally, you want to end up with a neat design, but a superficially neat design that makes no sense is not helpful.

Harry delegates to Joe when he is asked to do it himself, because we are calling through a reference to a FireChief.

Tom is also a FireChief, and we are calling through a reference to him as a FireChief, so he delegates to Harry; but when Harry is asked to do it in his role as a Firefighter (remember, the NumberOne property is a reference to a Firefighter), he does it himself, because we are now calling the method through that reference typed to Firefighter.

So our output looks like this:

Joe is putting out the fire!
Harry is putting out the fire!

That’s all very well, but we don’t actually want that restriction—the fire chief should be allowed to pass the work off to his subordinate as often as he likes, regardless of who he asked to do it.

What we actually want to do is to change the implementation based on the type of the object itself, not the variable we’re using to get at it. To do that we need to replace or override the default implementation in our base class with the one in our derived class. A quick glance at the C# spec shows us that there is a keyword to let us do just that: override.

Let’s switch to the override modifier on the FireChief implementation of the ExtinguishFire() method:

public override void ExtinguishFire()
{
    // Get our number one to put out the fire instead
    TellFirefighterToExtinguishFire(NumberOne);
}

Notice that we removed the new modifier and replaced it with override instead. But if you compile, you’ll see that we’re not quite done (i.e., we get a compiler error):

'FireChief.ExtinguishFire()': cannot override inherited member
'Firefighter.ExtinguishFire()' because it is not marked virtual, abstract,
or override

We’re not allowed to override the method with our own implementation because our base class has to say we’re allowed to do so. Fortunately, we wrote the base class, so we can do that (as the compiler error suggests) by marking the method in the base with the virtual modifier:

class Firefighter
{
    public virtual void ExtinguishFire()
    {
        Console.WriteLine("{0} is putting out the fire!", Name);
    }

    // ...
}

Why do we have this base-classes-opt-in system? Why is everything not virtual by default (like, say, Java)? Arguments continue on this very issue, but the designers of C# chose to go with the nonvirtual-by-default option. There are a couple of reasons for this: one has to do with implicit contracts, and another is related to versioning.

Note

There is also (potentially) a small performance overhead for virtual function dispatch, but this is negligible in most real-world scenarios. As always, test before optimizing for this!

We already saw how our public API is effectively a contract with our clients. With virtual functions, though, we are defining not only a contract for the caller, as usual, but also a contract for anyone who might choose to override that method. That requires more documentation, and a greater degree of control over how you implement the method.

Note

By declaring a method as virtual, the base class gives derived classes permission to replace whole pieces of its own innards. That’s a very powerful but very dangerous technique, rather like organ transplant surgery on an animal you’ve never seen before. Even a trained surgeon might balk at replacing the kidneys of a dromedary armed with nothing more than developer-quality documentation about the process.

For example, some method in your base class calls its MethodA, then its MethodB, to do some work. You then (perhaps unknowingly) rely on that ordering when you provide overrides for MethodA and MethodB. If a future version of the base class changes that ordering, you will break.

Let’s go back to our example to look at that in more detail, because it is really important.

First, let’s change the implementation of Firefighter.ExtinguishFire so that it makes use of a couple of helper methods: TurnOnHose and TrainHoseOnFire (see Example 4-8).

Let’s also simplify our Main function so that we can see what is going on, as shown in Example 4-9.

If we compile and run, we’ll see the following output:

Joe is putting out the fire!
Training the hose on the fire.
The fire is going out.

All is well so far, but what happens if we add a trainee firefighter into the mix? The trainee is extremely fastidious and follows his instructor’s guidelines to the letter. We’re going to make a class for him and override the TurnOnHose and TrainHoseOnFire methods so that the work is done in the trainee’s own particular idiom.

Hang on a moment, though! Our helper methods are private members. We can’t get at them, except from other members of our Firefighter class itself.

Before we can do anything, we need to make them accessible to derived classes.