C# 11 Inheritance

In the previous chapters, we looked at the basics of object-oriented programming in C#. Now that we have covered these basics, the next topic to be covered is that of class inheritance.

What is inheritance?

The concept of inheritance brings something of a real-world view to programming. It allows a class to be defined which has a number of characteristics and then other classes to be created which are derived from that class. The derived class inherits all of the features of the parent class and typically then adds some features of its own.

By deriving classes, we create what is often referred to as a class hierarchy. The class at the top of the hierarchy is known as the base class and the derived classes as subclasses. Any number of classes may be derived from a class. It is only possible for a derived class to inherit from one class. As such, C# is known as a single inheritance programming language. Classes need not only be derived from a base class. For example, a subclass can also inherit from another subclass.

An example of inheritance

As with most programming concepts the subject of inheritance in C# is perhaps best illustrated with an example. In the previous lesson, we created a class called BankAccount. The following declaration adds an additional field and property to the class allowing for the storage of the account balance:

public class BankAccount {
    private string _accountName;
    private int _accountNumber;
    private double _accountBalance = 0.00;

    public BankAccount(string accountName, int accountNumber, double accountBalance)
    { 
        _accountName = accountName;
        _accountNumber = accountNumber;
        _accountBalance = accountBalance;
    }

    public string AccountName { 
        get { return _accountName; } 
        set { _accountName = value; } 
    }

    public int AccountNumber { 
        get { return _accountNumber; } 
        set { _accountNumber = value; 
        } 
    }

    public double AccountBalance { 
        get { return _accountBalance; } 
        set { _accountBalance = value; } 
    }

    public void DisplayName() {
        System.Console.WriteLine($"Customer name is {AccountName}");
    }
}

This class does a good job of defining characteristics common to any type of bank account, such as account holder name, account number, and current balance. Imagine, however, that our banking program needs to support a number of specific types of bank account. For example, the bank might offer its customers an interest-bearing savings account. A savings account will have all the characteristics of our BankAccount class but would also need a way to store the prevailing interest rate. One option would be to create a brand new class from the ground up called SavingsAccount which duplicates everything we have in our BankAccount class, plus extra members needed for a savings account. An alternative and more efficient method is to derive a SavingsAccount class from the BankAccount class and then add the extra functionality into this subclass.

Creating a subclass in C#

Now that we have ascertained that we need to create a subclass of our BankAccount class we can take a look at the code necessary to achieve this. Subclasses are declared in the same way as any other class with the exception that the class name is followed by a colon (:) followed by the name of the class from which it is to inherit. With this in mind we can begin by creating our SavingsAccount class:

public class SavingsAccount : BankAccount 
{
}

We have now created a subclass of BankAccount called SavingsAccount, but at this point, the SavingsAccount class is no different than its parent class. Next, we need to add some new members to add the behavior we need:

public class SavingsAccount : BankAccount
{
    private double _interestRate;

    public SavingsAccount (string accountName, int accountNumber, double accountBalance, double interestRate)
    : base (accountName, accountNumber, accountBalance)
    {
        _interestRate = interestRate;
    }

    public double InterestRate {
        get { return _interestRate; }
        set { _interestRate = value; }
    }

    public double MonthlyInterest()
    {
        return (_interestRate * AccountBalance) / 12;
    }
}

We now have a new class called SavingsAccount which inherits all the members of the BankAccount class and adds some members of its own. In particular, we have added a new data member called interestRate which will store the interest rate paid on the account together with a new method to calculate the monthly interest.

Passing arguments to the base class constructor

Of particular significance is the constructor in the SavingsAccount declaration.

In the BankAccount base class we have a constructor which takes the account name and account number as arguments. In the SavingsAccount subclass, we need to accept two additional arguments; the balance and the interest rate. The : base code instructs C# to handle the name and number arguments using the constructor from the base class. The remaining two arguments are then passed to the SavingsAccount constructor:

public SavingsAccount (string accountName, int accountNumber, 
                        double accountBalance, double interestRate)
                             : base (accountName, accountNumber, accountBalance)

With our subclass complete we can now make use of it:

SavingsAccount account = new SavingsAccount(accountName: "John Smith", accountNumber: 12312, accountBalance: 1000.99, interestRate: 0.05);

System.Console.WriteLine ($"Interest this month = {account.MonthlyInterest()}");

account.DisplayName();

Overriding inherited methods

When using inheritance, it is not unusual to find a method in the parent class that almost does what you need, but requires modification to provide the precise functionality you require. One option in this scenario would be to ignore the inherited method and write a new method with an entirely new name. A better option is to override the inherited method and write a new version of it in the subclass.

Before proceeding with an example, there are three rules that must be obeyed when overriding a method. First, the overriding method in the subclass must take exactly the same number and type of parameters as the overridden method in the parent class. Second, the new method must have the same return type as the parent method. Finally, the method declaration in the base class must be declared using either virtualabstract, or override modifier. The topics of abstract and virtual class members will be covered in the next lesson.

In our BankAccount class we have a method named DisplayName which displays the account holder’s name. In our SavingsAccount subclass, we might also want to output the current interest rate assigned to the account. To achieve this, the first step is to declare the method in the BankAccount class as being virtual:

public class BankAccount 
{
.
.
    public virtual void DisplayName() {
        System.Console.WriteLine($"Customer name is {AccountName}");
    }
}

Next, we simply declare a new version of the DisplayBalance method in our SavingsAccount subclass, prefixed with the override modifier:

public class SavingsAccount : BankAccount
{
.
.
    public override void DisplayName() {
        System.Console.WriteLine($"Customer name is {AccountName}");
        System.Console.WriteLine($"Account is earning {InterestRate}% interest.");
    }
}

Now, when we call the DisplayName method on an instance of the SavingsAccount class, we get information about the customer’s interest rate:

SavingsAccount account = new SavingsAccount(accountName: "John Smith", accountNumber: 12312, accountBalance: 1000.99, interestRate: 0.05);

System.Console.WriteLine ($"Interest this month = {account.MonthlyInterest()}");

account.DisplayName();

It is also possible to make a call to the overridden method in the base class from within a subclass. The displayName method of the base class could, for example, be called to display the account holder’s name, before the interest rate is displayed, thereby eliminating further code duplication. This is achieved by using dot nation to call the method on the base object as follows:

public override void DisplayName() {
    base.DisplayName();
    System.Console.WriteLine($"Account is earning {interestRate}% interest.");
}

Categories