C Sharp

Inheritance

Inheritance relates to the programmer's ability to specify that one class has a kind-of relationship with another class. Through inheritance, you can create (or derive) a new class that's based on an existing class. You can then modify the class the way that you want and create new objects of the derived type. This ability is the essence of creating a class hierarchy. Outside of abstraction, inheritance is the most significant part of a system's overall design. A derived class is the new class being created, and the base class is the one from which the new class is derived. The newly derived class inherits all the members of the base class, thereby enabling you to reuse previous work.

NOTE
In C#, the issue of which base class members are inherited is controlled by the access modifiers used to define the member . I'll get into that level of detail in Chapter 5. For the purposes of this discussion, you can assume that a derived class will inherit all its base class members.

As an example of when and how to use inheritance, let's look back at our EmployeeApp example. In that example, we would almost certainly have different types of employees, such as salaried, contractor, and hourly. While all of these Employee objects would have a similar interface, they would in many cases function differently internally. For instance, the CalculatePay method would work differently for a salaried employee than it would for a contractor. However, you want the same CalculatePay interface for your users regardless of employee type.

If you're new to object-oriented programming, you might be wondering, "Why do I even need objects here? Why can't I simply have an EMPLOYEE structure with an employee type member and then have a function similar to this?" -

Double CalculatePay(EMPLOYEE* pEmployee, int iHoursWorked)
{
    // Validate pEmployee pointer.
    if (pEmployee->type == SALARIED)
    {
        // Do W-2 employee processing.
    }
    else if (pEmployee->type == CONTRACTOR)
    {
        // Do 1099 processing.
    }
    else if (pEmployee-> == HOURLY)
    {
        // Do hourly processing.
    }
    else
    {
        // Do corp-to-corp processing.
    }
    // Return the value from one of the
    // compound statements above.
}

This code has a couple of problems. First, the success of the CalculatePay function is tightly linked to the EMPLOYEE structure. As I mentioned earlier, tight coupling like this is a problem because any modification to the EMPLOYEE structure will break this code. As an object-oriented programmer, the last thing you want to do is burden the users of your class with needing to know the intricate details of your class's design. That would be like a vending machine manufacturer requiring you to understand the internal mechanics of the vending machine before you can purchase a soda.

Second, the code doesn't promote reuse. Once you begin to see how inheritance promotes reuse, you realize that classes and objects are good things. In this case, you would simply define all the members for the base class that would function the same regardless of employee type. Any derived class would then inherit this functionality and change anything necessary. Here's how that would look in C#: -

class Employee
{
    public Employee(string firstName, string lastName,
                    int age, double payRate)
    {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
        this.payRate = payRate;
    }
    protected string firstName;
    protected string lastName;
    protected int age;
    protected double payRate;
    public double CalculatePay(int hoursWorked)
    {
        // Calculate pay here.
        return (payRate * (double)hoursWorked);
    }
}
class SalariedEmployee : Employee
{
    public string SocialSecurityNumber;
    public void CalculatePay (int hoursWorked)
    {
        // Calculate pay for a W-2 employee.
    }
}
class ContractEmployee : Employee
{
    public string FederalTaxId;
    public void CalculatePay (int hoursWorked)
    {
        // Calculate pay for a contract employee.
    }
}

Three features of the preceding example are worth noting: -

  • The base class, Employee, defines a string called EmployeeId that is inherited by both the SalariedEmployee and the ContractEmployee classes. The two derived classes do nothing to get this member-they inherit it automatically as a by-product of being derived from the Employee class.
  • Both derived classes implement their own versions of CalculatePay. However, you'll notice that they both inherited the interface, and although they changed the internals to suit their specific needs, the user's code remains the same.
  • Both derived classes added members to the members that were inherited from the base class. The SalariedEmployee class defines a SocialSecurityNumber string, and the ContractEmployee class includes a definition for a FederalTaxId member.

You've seen in this small example that inheritance enables you to reuse code by inheriting functionality from base classes. And it goes even further, allowing you to extend the class above and beyond that point by adding your own variables and methods.