BlackWasp
C# Programming
.NET 1.1

C# Inheritance

The eighteenth part of the C# Object-Oriented Programming tutorial begins the discussion of the use of inheritance.  This is a powerful object-oriented concept that permits the creation of hierarchical groups of classes that share common functionality.

What is Inheritance?

Inheritance is one of the most important, and most powerful, of the object-oriented programming concepts.  Using inheritance, one class can be derived from another.  The derived class, also known as the child class or subclass, inherits functionality from the base class, also known as the parent class or superclass.  The subclass can add methods and properties or modify the functionality that it has inherited to provide a more specialised version of the base class.

Using inheritance, classes become grouped together in a hierarchical tree structure.  The more generalised classes appear at the root of the hierarchy with the more specific classes appearing on the tree's branches.  This categorisation of classes into related types is why inheritance is sometimes referred to as generalisation (or generalization).

When using inheritance, all of the base class' methods, properties, events, indexers, operators and some internal variables are all automatically provided to every subclass.  As this functionality is automatically available in the child class, there is no need to duplicate code.  This reduces maintenance problems, as changes need only to be made in one class, rather than throughout a series of related classes.

Polymorphism

Another key concept of object-oriented programming is polymorphism.  This is the ability for an object to change its behaviour according to how it is being used.  This is an important part of inheritance and means that a subclass can be used as if it were an instance of its base class.  If, for example, you were to create a class with generic functionality for processing the control of vehicles, and you created a more specialised subclass for cars, the Car objects could be passed to methods that expected a Vehicle object.  The method would operate on the using the public interface of the Vehicle class.  However, the underlying functionality of the Car class would be used.  The same routine could be used to process bicycles, buses and other vehicle types.

This type of polymorphism relies heavily on the encapsulation principle.  As the method using the Vehicle object is aware only of the Vehicle class' public interface and not its internal workings, it is easy to substitute a Car object because cars are just a special type of vehicle.

Single Inheritance and Multiple Inheritance

There are two types of inheritance used in modern programming languages.  C# and other .NET languages use single inheritance.  This means that a subclass may only inherit functionality from a single base class.

Multiple inheritance permits a subclass to have two or more superclasses.  In this situation the derived class inherits functionality from several base classes.  Multiple inheritance is not supported by C# or the .NET framework.  However, this does not stop a class from providing many public interfaces as will be seen in a later article.

Demonstrating Inheritance

To demonstrate the use of inheritance with real-world objects, during the rest of this article we will create an example project based around the Vehicle class.  This general class will provide methods and properties that all vehicles provide.  We will then create subclasses of Vehicle with more specialised functionality.

To begin, create a new console application named "InheritanceDemo".  Create a class file named "Vehicle" and add the following code to the new class to provide a Speed property and Accelerate and Decelerate methods to all vehicles:

private int _speed;     // Miles per hour


public int Speed
{
    get
    {
        return _speed;
    }
}


public void Accelerate(int mph)
{
    _speed += mph;
}


public void Decelerate(int mph)
{
    _speed -= mph;
}

The new Vehicle class can be instantiated and tested in its own right.  To ensure the class is working correctly, we can modify the Main method of the program to test the methods and the property:

static void Main(string[] args)
{
    Vehicle v = new Vehicle();
    Console.WriteLine("Speed: {0}mph", v.Speed);    // Outputs "Speed 0mph"
    v.Accelerate(25);
    Console.WriteLine("Speed: {0}mph", v.Speed);    // Outputs "Speed 25mph"
    v.Decelerate(15);
    Console.WriteLine("Speed: {0}mph", v.Speed);    // Outputs "Speed 10mph"
}

Creating a Derived Class

Syntax

The syntax for declaring a derived class is similar to that for any other standard class.  To specify the base class to inherit from, its name is appended to the subclass declaration, separated by a colon (:) as follows:

class subclass : baseclass

The MotorVehicle Class

We can now create a class to represent motor vehicles such as cars, buses, etc.  As this class is a specialised vehicle that incorporates all of the functionality of the Vehicle class, we can derive the class from Vehicle.  Add a new class file to the project and name it "MotorVehicle".  Modify the standard class declaration to add the inheritance relationship.

class MotorVehicle : Vehicle
{
}

Although the new class has no code contained within its code block, it already includes the properties and methods defined in its parent class.  We can demonstrate this easily by modifying the Main method of the program as follows:

static void Main(string[] args)
{
    MotorVehicle v = new MotorVehicle();
    Console.WriteLine("Speed: {0}mph", v.Speed);    // Outputs "Speed 0mph"
    v.Accelerate(25);
    Console.WriteLine("Speed: {0}mph", v.Speed);    // Outputs "Speed 25mph"
    v.Decelerate(15);
    Console.WriteLine("Speed: {0}mph", v.Speed);    // Outputs "Speed 10mph"
}

The new main method is essentially the same as the previous example except that an instance of the MotorVehicle class is used rather than a Vehicle object.  The output of the program is identical to the earlier code because the functionality used has been inherited without modification.

For later use in this article, create another class file for bicycles named "Bicycle".  Bicycles can also accelerate and decelerate so modify the class so that it also inherits its functionality from the Vehicle class.  You can test this class with a simple modification to the Main method.

class Bicycle : Vehicle
{
}

Adding Specialised Functionality to a Derived Class

So far, the MotorVehicle and Bicycle classes are essentially identical to the underlying Vehicle class.  The value of specialisation provided by inheritance hierarchies is that the derived classes can implement addition functionality.  In the case of motor vehicles, fuel is used.  We can therefore add properties to identify how many gallons of fuel are present in the vehicle and how large the fuel tank is.  We can also create a method to call when the motor vehicle is refuelled.  This functionality will be present to motor vehicles but not bicycles.

To add the properties and method, include the following code in the MotorVehicle class.  The FuelRemaining property is read-only as it can only be changed via the Refuel method, which returns the number of gallons of fuel added to completely fill the tank.

private float _fuelRemaining;
private float _tankSize;


public float FuelRemaining
{
    get
    {
        return _fuelRemaining;
    }
}


public float TankSize
{
    get
    {
        return _fuelRemaining;
    }
    set
    {
        _tankSize = value;
    }
}


public float Refuel()
{
    float fuelRequired = _tankSize - _fuelRemaining;
    _fuelRemaining = _tankSize;
    return fuelRequired;
}

To give the Bicycle class additional functionality, let's add a method that rings the bell.  To indicate that the rider has used the bell we will simply output a string to the console.  Add the following method to the Bicycle class:

public void RingBell()
{
    Console.WriteLine("Ring!");
}

The two derived classes now include all of the functionality of the Vehicle class and the addition items added.  To demonstrate the use of the two subclasses, modify the Main method of the program as follows:

MotorVehicle car = new MotorVehicle();
car.TankSize = 11;
Console.WriteLine("Fuel: {0}g", car.FuelRemaining); // Outputs "0g"
car.Refuel();
Console.WriteLine("Fuel: {0}g", car.FuelRemaining); // Outputs "11g"

Bicycle bike = new Bicycle();
bike.RingBell();                                     // Outputs "Ring!"

Overriding Base Class Functionality

Inheritance is not limited to additional functionality.  It is common for derived classes to modify the behaviour of superclass methods and properties according to the use of the subclass.  In our example, all vehicles should provide the ability to indicate when they are planning to turn left or right.  The method of indication will vary according to the type of vehicle.

When a method in a base class is designed to be overridden by its subclasses, the method is marked as such using the virtual keyword.  In the following virtual method, the basic Vehicle class will simply output a message describing the indication direction given.  This will be overridden in the MotorVehicle and Bicycle classes.

Add the following method to the Vehicle class:

public void Indicate(bool turningLeft)
{
    if (turningLeft)
        Console.WriteLine("Turning left");
    else
        Console.WriteLine("Turning right");
}

In the MotorVehicle class, the basic outputted message will be overridden.  This is achieved by creating a method with the same scope, name and parameters, or signature, as the base class method, and prefixing the method's return type with the override keyword.

Add the following method to the MotorVehicle class:

public override void Indicate(bool turningLeft)
{
    if (turningLeft)
        Console.WriteLine("Flashing left indicator");
    else
        Console.WriteLine("Flashing right indicator");
}

For bicycles, the rider will raise their arm to indicate that they are turning so add the following code to the Bicycle class:

public override void Indicate(bool turningLeft)
{
    if (turningLeft)
        Console.WriteLine("Raising left arm");
    else
        Console.WriteLine("Raising right arm");
}

The two subclasses now share the same Indicate method as part of their public interface.  However, the internal functionality of the two methods is different as can be seen by executing the following code:

MotorVehicle car = new MotorVehicle();
car.Indicate(true);                     // Outputs "Flashing left indicator"

Bicycle bike = new Bicycle();
bike.Indicate(true);                    // Outputs "Raising left arm"

Calling Base Class Functionality

In the previous example, a method from the base class was overridden by a derived method with a matching signature.  Often the functionality from parent class is required but needs to be extended by the child class.  By using the special base object, a subclass can access base class' functions.  Calling the base class method from within an overridden member can be used to combine functionality.  In fact, a subclass can access any of the base class members via the base object.

This can be demonstrated by modifying the Indicate method of the Bicycle class.  In the following example, the base class method is called first before the subclass functionality executes.

public override void Indicate(bool turningLeft)
{
    base.Indicate(turningLeft);

    if (turningLeft)
        Console.WriteLine("Raising left arm");
    else
        Console.WriteLine("Raising right arm");
}

Calling the modified method shows the results of both operations:

Bicycle bike = new Bicycle();
bike.Indicate(true);

/* OUTPUT

Turning left
Raising left arm

*/

Polymorphism and Inheritance

Polymorphism is an important object-oriented programming concept.  Polymorphism is the ability for an object to change its public interface according to how it is being used.  When using inheritance, polymorphism is achieved when an object of a derived class is substituted where an instance of its parent class is expected.  This uses a process known as upcasting.  With upcasting, the object of the more specialised class is implicitly cast to the base class type required.

In the case of the MotorVehicle and Bicycle class, instances of either type can be held within a Vehicle variable or passed as a Vehicle method parameter.  Any users of the object will only have access to the public interface defined by the base class but when these members are accessed, the underlying members of the derived class will be executed.  So if we require a standard routine to operate on any type of vehicle, we can use a Vehicle variable in the knowledge that internally the subclass' functionality will run.

To demonstrate this, we will create some basic Vehicle variables but assign MotorVehicle or Bicycle objects to them.  We can then observe the results, as in the following modified Main method:

Vehicle car = new MotorVehicle();
Vehicle bike = new Bicycle();

car.Indicate(true);
bike.Indicate(true);

/* OUTPUT

Flashing left indicator
Turning left
Raising left arm

*/

Another way of demonstrating this polymorphism behaviour is to use a method in the Program class.  The method shown below accepts a Vehicle parameter that can be substituted with a MotorVehicle or Bicycle.

static void Main(string[] args)
{
    Vehicle car = new MotorVehicle();
    Vehicle bike = new Bicycle();

    IndicateLeft(car);
    IndicateLeft(bike);

    /* OUTPUT

    Flashing left indicator
    Turning left
    Raising left arm

    */
}


static void IndicateLeft(Vehicle vehicle)
{
    vehicle.Indicate(true);
}

As can be seen from the previous examples, the conversion between a child class and its parent is performed implicitly.  The reverse is not true.  If, for example, a Vehicle variable contained a MotorVehicle object, it could not be assigned directly to a MotorVehicle variable.  In this case, an explicit cast would be required as shown below:

Vehicle car = new MotorVehicle();
MotorVehicle theCar = (MotorVehicle)car;    // Explicit cast

This explicit casting to a more specialised class is the opposite of the upcasting concept described earlier.  As such, it is known as downcasting.

Name Hiding

Name hiding is a similar concept to overriding.  With name hiding, a new version of a member or variable is created in a child class to replace or extend the parent's version.  However, rather than using the override keyword, the new keyword prefixes the declaration.

The main functional difference between name hiding and overriding is that the polymorphism effect is lost.  When a derived class instance is assigned to a variable of the base class type, the functionality of the base class member is executed rather than the functionality of the method causing the name hiding.

Name hiding is useful if you do not have control over a base class.  This may be because it is part of a library for which you do not own the source code.  If a member of a base class is not marked as virtual then you may not override it.  However, even non-virtual members may be hidden.

In the next example, the MotorVehicle's Indicate method is replaced using name hiding rather than overriding.  Note the effect of calling the method differs according to the data type of the variable that the object resides within.  To demonstrate, first modify the Indicate method of the MotorVehicle class as follows:

public new void Indicate(bool turningLeft)
{
    if (turningLeft)
        Console.WriteLine("Flashing left indicator");
    else
        Console.WriteLine("Flashing right indicator");
}

Now modify the Main method and execute it to see the results.  You can see that the output is different in each call to the Indicate method, something that does not occur with method overriding:

Vehicle car1 = new MotorVehicle();
car1.Indicate(true);                        // Outputs "Turning left"

MotorVehicle car2 = new MotorVehicle();
car2.Indicate(true);                        // "Flashing left indicator"

NB: After testing the above sample, revert the Indicate method to use override rather than new.

Protected Scope

Earlier in this tutorial we examined the use of public and private access modifiers to modify the scope of variables, methods, properties, etc.  To recap, items within classes that have a public scope are visible outside of the class.  Items marked as private are visible only within the scope of a class.

Another access modifier exists for use in inheritance hierarchies.  This is the protected access modifier.  A protected item is generally as visible as if it were private except for one special case.  If an item in a base class is declared as protected, it is visible to its subclasses.

To demonstrate this, add the following property to the MotorVehicle class.  The property calculates the fuel efficiency of the motor vehicle in miles per gallon, with the calculation base upon the current speed of the vehicle.

public int Mpg
{
    get
    {
        if (_speed == 0)
            return 0;
        else if (_speed < 20)
            return 50;
        else if (_speed < 50)
            return 40;
        else
            return 35;
    }
}

If you attempt to compile the application you will receive three compiler errors.  Each indicates that the "_speed" variable is inaccessible due to its protection level.  In other words, because "_speed" is declared in the Vehicle class as a private variable it cannot be used within the MotorVehicle subclass.  However, if you modify the variable in the Vehicle class to be protected, it will be visible to the subclass and the program will compile correctly.

protected int _speed;           // Miles per hour

Multi-Level Hierarchies

The benefits of inheritance are not limited to the two-level hierarchies that have been shown in the above examples.  It is possible to add further subclasses to create multi-level inheritance.  This allows each child to add more specialised functionality whilst retaining the members of its parents, grandparents and so on.

In the case of the vehicles example, it would be likely that you would want separate classes for cars, motorcycles and buses.  Each of these could inherit from the MotorVehicle class for a three-level hierarchy.  A class for lorries with a property for their load capacity could be as follows:

class Lorry : MotorVehicle
{
    private int _capacity;      // Cubic metres


    public int Capacity
    {
        get
        {
            return _capacity;
        }
        set
        {
            _capacity = value;
        }
    }
}

As the Lorry class is derived from the MotorVehicle class, which in turn is a subclass of Vehicle, the functionality from all three of these classes would be available to any Lorry object.

Preventing Inheritance

In some situations you may want to prevent inheritance.  For example, you may create a class for use by other programmers where the class contains security functionality that must not be modified.  To indicate that a class may not be inherited you can prefix the class definition with the sealed keyword.

To demonstrate, modify the declaration of the Vehicle class as follows:

sealed class Vehicle

The use of the sealed keyword prevents the MotorVehicle and Bicycle classes from inheriting functionality from Vehicle, so the application no longer compiles.  You also receive a compiler warning highlighting that the "_speed" variable is protected.  This warning appears because including a protected variable in a sealed class suggests that a mistake has been made.  In any case, a protected variable may not be inherited from a sealed class.

A Final Note on Events and Inheritance

Events that are declared in a base class are still available for subscription from derived classes.  However, the derived class is not able to raise an event defined in the base class.  In the earlier article describing events, it was suggested that a centralised method be created that raises an event and that this method be marked as virtual.  You can now see the benefit of this approach, as the derived class is able to call this method and indirectly raise the event.  Alternatively, the subclass can override the event-calling method to change how events are raised.

Link to this Page15 March 2008
RSS RSS Feed
77 users on-line