You will learn about the following in this chapter:
The connection between taxonomical type hierarchies and object-oriented class hierarchies
Why inheritance is advantageous when implementing classes that naturally fit into a class hierarchy
The syntax needed for implementing a class hierarchy in C# and, in particular, how to derive a class from a base class
Virtual functions
The difference between overriding a method (using the override keyword) and re-implementing a new method (using the new keyword)
How access modifiers and class hierarchies interact
How to preserve the rules of encapsulation while implementing inheritance
The protected and internal protected access modifiers
Constructors and inheritance
The base-access construct
The importance of inheritance when reusing class libraries in general and the .NET Framework class library in particular
Method overriding versus method overloading
An important aspect of object-oriented programming is its extensive support for code reuse, which keeps programmers from the time-consuming and error-prone process of re-implementing software functionality that has already been written and thoroughly tested, often by highly competent programmers.
You have already seen many examples of code reuse in this book, especially of the components in the .NET Framework class library. One of the underlying concepts for the type of code reuse you have experienced so far is called aggregation, which was discussed as part of the SimpleElevatorSimulation.cs source code in Listing 5.1 of Chapter 5, "Your First Object-Oriented C# Program." You might remember that aggregation let us piece together a class from other types. In our example, the Elevator class was composed of three int types and one Person class. Later, in Chapter 10, "Arrays Part I: Array Essentials," you saw how we could reuse .NET's Array class (line 55 of the BankSimulation.cs program in Listing 10.14) to create a bank with an array of bank accounts.
Inheritance is another important mechanism found in object-oriented programming to facilitate code reuse. Inheritance allows us to define a new class by extending an already existing class. The derived class inherits the class members from the old class for free; it is then up to the programmer to specify the differences between the old class and the new class. This is done by
Adding new class members (function members and data members) to the derived class, which will then consist of the new members plus the members inherited from the old class
Modify, if needed, how some of the inherited function members behave in the derived class by providing new implementations for those function members
So, whereas aggregation let us compose a Car class from its parts (Engine, Wheel, Gear-box, SteeringWheel, and other classes), inheritance let us extend a general Car class to, for example, create a more specialized SportsCar class by stating the differences between a Car and a SportsCar (better suspension, engine more powerful, and so on) while reusing the parts they have in common (four wheels, steering wheel, front seats, and so on). Often, the Car class has an Engine, has a Wheel, and so on, whereas SportsCar is a Car.
Inheritance forms the basis of another powerful object-oriented concept called polymorphism, which allows a program to access objects of different types through just one reference variable. This allows the same function name to have several different implementations during one runtime.
Inheritance and polymorphism have profoundly changed software construction and put tremendous power in the hands of the programmer.
This chapter introduces the basic concepts of inheritance. Chapter 17, "Inheritance Part II: Abstract Functions, Polymorphism, and Interfaces," builds on this knowledge.