The Inheritance Problem
Article Index
The Inheritance Problem
Solution

Inheritance and function overloading don't play well together in C#. See if you can figure out the reasons that this puzzle doesn't work as you might expect.

 

Background

Object Oriented programming is wonderful and one of its most wonderful attributes is the ability to create new classes that inherit all of the methods and properties of existing classes.

Today there may be some worries about using inheritance and even a move towards claiming that it isn't a particularly safe construct but many programmers do make use of it within their projects because it is better than Copy and Paste inheritance that you often see in use when the Interface approach is used.

So leaving asside questions of the advisability of using inheritance let's see how this particular puzzle came about.

C# takes a very standard approach to objects. You can create a base class:

class Base
{
public void MyMethod(int i)
{
  MessageBox.Show("Base method Int");
}
}

create a derived class:

Class Derived : Base
{    
}

and even if the Derived class is empty it still has all of the properties and methods of the base class. So you can call MyMethod using an object of the Derived class:

Derived MyObject = new Derived();
MyObject.MyMethod(1);

Another useful feature of C#, although it isn't an object oriented one is function overloading - which always sounds as if it is a sort of cruely to functions. It isn't because it is incredibly useful.

Functions are not simply defined by their name i.e. MyMethod but the types of their parameters or signature. That is MyMethod is MyMethod(int). What this means is that you can define lots of methods with same name as long as their parameter lists i.e. signatures are different. Notice that the return type plays no role in the signature.

If you make a call to an overloaded function then the function definition with the most specific match to the call signature is the one that is actually used. For example, if we change the defintion of the Base class to:

class Base
{
public void MyMethod(int i)
{
  MessageBox.Show("Base method Int");
}
public void MyMethod(object i)
{
  MessageBox.Show("Base Method Object");
}
}

And make a the call

Derived MyObject = new Derived();
MyObject.MyMethod(1);

then the call signature MyMethod(int) matched MyMethod(object) but it also matches the more specific MyMethod(int) and this is the overloaded version that is used.

Notice that the selection process make use of the inheritance hiearachy to work out which function should be used. In general if multiple overloaded function match a call signature then the one that has parameters highest i.e.. most derived in the inheritance tree is used.

For example the call

Derived MyObject = new Derived();
MyObject.MyMethod(1.0);

results in a call to MyMethod(object) because a float isn't derived from an int but from a object. It might make more sense to you if the int signature was used but this is not how the rigorous logic of inheritance works.

Puzzle

Now we come to the core of the puzzle.

As always the code presented here has been stripped down to its essentials. In the real world example the code was more complex and hence hid the problem much more effectively.

The design of the system involved a small number of derived classes. The Base class had two overloaded methods:

class Base
{
public void MyMethod(int i)
{
  MessageBox.Show("Base method Int");
}

public void MyMethod(object i)
{
  MessageBox.Show("Base Method Object");
}
}

The Derived class had lots of additional methods (left out of the example) but it relied on inheritance for the MyMethod overloaded function.

class Derived : Base
{   
}

Of course all of this works perfectly. The call

Derived MyObject = new Derived();
MyObject.MyMethod(1);

results in a call to MyMethod(int) as provided by the Base class i.e. it displays Base method int.

The next step was a little strange but seemed perfectly reasonably given the way the project's class hierarchy was developing.

It was noticed that logically only the derived class actually needed to support the MyMethod(object) call and so a refactoring was performed to move the method from the Base to the Derived class:

class Base
{
public void MyMethod(int i)
{
  MessageBox.Show("Base method Int");
}
}
class Derived : Base
{
public void MyMethod(object i)
{
  MessageBox.Show("Derived Method Object");
}
}

 

Now guess what -

the call to MyMethod(1) that previously correctly called MyMethod(int) in the Base class now calls MyMethod(object) in the Derived class.

Why?

And is there a fix?

Turn to the next page when you are ready to find out.

 

Banner

<ASIN:1449380344>

<ASIN:1430225378>
<ASIN:0470447613>

<ASIN:1933988924>