Learning C# and OOP, Inheritance
Baldwin shows you how to extend an existing class to create a new class. The new class is the blueprint for a new type. This is the mechanism for class inheritance in C#. Inheritance provides a formal mechanism for code reuse.
Published: November 3, 2002
C# Programming Notes # 110
This lesson is one of a series of lessons designed to teach you about the essence of Object-Oriented Programming (OOP) using C#.
The first lesson in the group was entitled Learning C# and OOP, Getting Started, Objects and Encapsulation. That lesson, and each of the lessons following that one, has provided explanations of certain aspects of the essence of Object-Oriented Programming using C#. The previous lesson was entitled Learning C# and OOP, Indexers, Part 2.
Essential aspects of OOP in C#
This miniseries will describe and discuss the necessary and significant aspects of OOP using C#.
I will provide that information in a relatively high-level format, devoid of any requirement to know detailed C# syntax. In those cases where an understanding of C# syntax is required, I will provide the necessary syntax information in the form of sidebars or other obvious formats.
Therefore, if you have a general understanding of computer programming, you should be able to read and understand the lessons in this miniseries, even if you don't have a strong background in the C# programming language.
You may find it useful to open another copy of this lesson in a separate browser window. That will make it easier for you to scroll back and forth among the different listings while you are reading about them.
I recommend that you also study the other lessons in my extensive collection of online programming tutorials. You will find a consolidated index at www.DickBaldwin.com.
Extending a class
This lesson shows you how to extend an existing class to create a new class. The new class is the blueprint for a new type.
Inheritance and code reuse
The existing class is often called the superclass (or base class) and the new class is often called the subclass (or derived class). This is the mechanism for class inheritance in C#. Inheritance provides a formal mechanism for code reuse.
Classes form a hierarchy. The subclass inherits all of the variables and all of the methods defined in the superclass, and its superclass, etc.
Car radios with tape players
A class from a previous lesson (whose objects represent car radios) is extended in this lesson to define a new class, (whose objects represent extended car radios that contain tape players).
Sending messages to the object
Objects of the new class know how to respond to messages for inserting, playing, and removing a tape, in addition to those messages appropriate for objects of the original Radio class.
Method overriding is used to modify the behavior of an inherited method of the Radio class named playStation. This causes the playStation method to behave appropriately when a tape has been inserted into the tape player.
The three pillars of OOP
In an earlier lesson, I explained that most books on OOP will tell you that in order to understand OOP, you must understand the following three concepts:
Previous lessons in this series provided an explanation of encapsulation.
This lesson will provide an explanation of inheritance. I will use a simple program to explain the concept of inheritance.
Polymorphism is the most complex of the three pillars, and will be fully explained in future lessons. However, it will be necessary to touch on polymorphism based on method overriding in this lesson as well.
A new data type
Whenever you define a class in C#, you cause a new data type to become available to the program. Therefore, whenever you need a new data type, you can define a new class to make that type available.
Creating a new type
Defining a new class (to create a new type) can involve a lot of effort. Sometimes you have an option that can greatly reduce the effort required to create your new type.
Extending a class
If a class (type) already exists that is close to what you need, you can often extend that class to produce a new class that meets your needs.
In many cases, this will require much less effort than the effort required to start from scratch and define a new class to establish a new type.
The ability to extend one class into another new class is the essence of inheritance.
According to the current jargon, the new class is called the subclass and the class that was extended is called the superclass.
What is inherited?
When you define a class that extends another class, an object instantiated from your new class will contain all of the methods and all of the variables defined in your new class. The object will also contain all of the methods and all of the variables defined in all of the superclasses of your new class.
Therefore, inheritance often makes it possible to define a new class with a minimum requirement to write new code by formally reusing the code that was previously written into the superclasses.
The behavior of the methods
The behavior of the methods defined in a superclass and inherited into your new class may, or may not, be appropriate for an object instantiated from your new class. If those methods are appropriate, you can simply leave them alone.
Overriding to change behavior
If the behavior of one or more methods defined in a superclass and inherited into your new class is not appropriate for an object of your new class, you can change that behavior by overriding the method in your new class.
How do you override a method?
To override a method in your new class, simply reproduce the name, argument list, and return type of the original method in a new method definition in your new class. Then provide a body for the new method. Write code in that body to cause the behavior of the overridden method to be appropriate for an object of your new class.
Any method that is declared virtual in the superclass can be overridden in a subclass.
Overriding versus overloading
Don't confuse method overriding with method overloading.
An overloaded method differs from the original method in the number and/or type of its arguments. A different return type is permitted for an overloaded method, but is not sufficient by itself to distinguish an overloaded method from the original.
I will have much more to say about overloaded methods in a future lesson.
Car radios with built-in tape players
This lesson presents a sample program that duplicates the functionality of the program named Radio01a discussed in a previous lesson. A class named Radio is used to define the specifics of objects intended to simulate car radios.
A class named Combo extends the Radio class to define the specifics of objects intended to simulate extended car radios having built-in tape players.
The Radio class
A previous program defined a class named Radio. Objects instantiated from the Radio class (see the previous lessons for a discussion of instantiating objects) were intended to simulate car radios. (Note that the car radios simulated by objects of the Radio class didn't have built-in tape players.)
The Combo class
In this lesson, I will use inheritance to extend the Radio class into a new class named Combo. Objects instantiated from the Combo class are intended to simulate car radios with a built-in tape player.
A complete listing of the new program is shown in Listing 8 near the end of the lesson.
Overridden playStation method
In particular, a method named playStation, defined in the superclass named Radio, is overridden in the subclass named Combo.
The original version of the playStation method in the superclass supports only radio operations. (It doesn't support tape operations.)
The overridden version of the playStation method defined in the subclass supports both radio operations and tape operations.
Will discuss in fragments
As usual, I will discuss this program in fragments. I will begin my discussion with the definition of the new class named Combo. Then I will come back and discuss the class named Radio and the driver class named Radio03.
The combo class
The code fragment in Listing 1 shows the beginning of the class named Combo.
that the boldface was added for emphasis only. C# source code does
not use boldface, Italics, or other such typographical features.)
Two new items
There are two new concepts illustrated in Listing 1 that you did not see in the code in the previous lessons.
Combo extends Radio
First, the class named Combo extends or inherits from the class named Radio.
What does this mean?
This means that an object instantiated from the Combo class will contain all of the variables and all the methods defined in the Combo class, plus all the variables and methods defined in the Radio class, and its superclasses. (The variables and methods of the superclass are inherited into the subclass.)
An explicit constructor
The second new concept illustrated in Listing 1 is that the class named Combo defines an explicit constructor.
Defining a constructor is optional
When defining a new class, it is not necessary to define a constructor. If you don't define a constructor, a default constructor will be provided automatically. That was the case for the programs in the previous lessons in this miniseries.
Why define a constructor?
The intended purpose of a constructor is to initialize the instance variables belonging to the new object.
However, constructors can do other things as well. In this case, I used an explicit constructor to display a message when an object is instantiated from the class named Combo.
Brief discussion of constructors
I'm not going to discuss constructors in detail at this point. However, I will give you a few rules regarding constructors.
A boolean instance variable
Although not necessarily new to this lesson, the code in Listing 1 also declares and initializes an instance variable of type bool named tapeIn. This instance variable will be used later to deal with the logic of inserting tapes, removing tapes, etc.
The new class named Combo defines three instance methods, each of which has to do with the handling of tape in the tape player:
(If you feel ambitious, you could upgrade this class even further to add features such as rewind, fast forward, pause, etc.)
In addition, the Combo class provides an overridden version of the playStation method, which it inherits from the Radio class.
The insertTape method
The entire method named insertTape is shown in Listing 2.
This is the method that is used to simulate the insertion of a tape by
The most significant thing about the code in Listing 2 is the assignment of the true value to the boolean variable named tapeIn. (The variable named tapeIn is declared and initialized in Listing 1.)
Other than setting the value of the tapeIn variable to true, the code in Listing 2 simply prints some messages to indicate what is going on.
What is tapeIn used for?
As you will see shortly, the value of the variable named tapeIn is used to determine when it is possible to play the tape and when it is possible to play the radio. (It is not possible to play both the tape and the radio simultaneously.)
According to the logic:
The removeTape method
The removeTape method of the Combo class is shown in Listing
3. Its behavior is pretty much the reverse of the insertTape
method, so I won't discuss it further.
The playTape method
Listing 4 shows the method named playTape defined in the new
Confirm that the tape is ready
The invocation of the method named playTape can be thought of as sending a message to the Combo object to play the tape.
The code in the playTape method checks to confirm that the value stored in the tapeIn variable is true before implementing the incoming message to play the tape.
If tapeIn is false, an error message is displayed advising the user to insert the tape first.
If tapeIn is true, the method prints a message indicating that the tape is playing.
The overridden playStation method
The Combo class overrides the playStation method, which it inherits from the Radio class. The overridden version of the playStation method is shown in Listing 5.
As you will see when we examine the Radio class, this version
of the method duplicates the signature of the playStation method
in the superclass named
Radio, but provides a different body.
An override must be declared
This version of the playStation method also declares that the method overrides the inherited version of the method, which must be declared virtual.
Aware of the tape system
The original version of the playStation method in the Radio class is unaware of the existence of the tape system. However, this overridden version of the method is aware of the existence of the tape system and behaves accordingly.
Depending on the value of the variable named tapeIn, this method will either tune and play a radio station, or display a message instructing the user to remove the tape.
Which version of playStation is executed?
When the playStation method is invoked on an object of the Radio class, the version of the playStation method defined in the Radio class will be executed.
When the playStation method is invoked on an object of the Combo class, the overridden version of the method defined in the Combo class (and not the original version defined in the Radio class) will be executed.
Listing 5 shows the definition of the Radio class. I provide
it here for easy referral. This class definition is unchanged from
the version that you saw in a previous lesson.
Combo overrides playStation
As you saw earlier, the class named Combo extends this class and overrides the method named playStation, shown in boldface in Listing 6.
Doesn't support tape operations
If you examine the code for the playStation method in the Radio class in Listing 6, you will see that it assumes radio operations only and doesn't support tape operations. That is the reason that it needs to be overridden.
Must be virtual for override
You should also note that the playStation method defined in the Radio class is declared virtual.
As mentioned earlier, in C#, only virtual methods can be overridden.
So, what's the big deal with inheritance?
The use of inheritance eliminated the need for me to define a new class that reproduces all of the code in the class named Radio.
I was able to define a new class for an improved radio, (which supports a tape player), simply by extending the Radio class and overriding one of its methods.
There are lots of reasons for using inheritance, and this is only one of them.
The driver class
The new driver class named Radio03 is shown in Listing 7.
New object of the Combo class
The most significant change in this class (relative to the driver class named Radio01a in a previous lesson) is the single statement in boldface Italics. Note that unlike the previous program, this program instantiates a new object of the Combo class (instead of the Radio class).
All of the other new code in Listing 8 is used to send messages to the new object in order to exercise its behavior.
Invoke the playStation method
I present this code here to emphasize that this code instantiates a new object of the Combo class. This assures that the overridden version of the method named playStation will be executed by the boldface statements in Listing 7 that invoke the playStation method.
That object responds to those messages by producing the following output on the computer screen:
Combo object constructed
Leave as an exercise for the student
As the old saying goes, I will leave it as an exercise for the student to correlate the messages in Listing 7 with the output shown above.
Inheritance provides a formal mechanism for code reuse.
Extending an existing class often provides an easy way to create a new type. This is primarily true when an existing class creates a type whose features are close to, but not identical to the features needed in the new type.
When an existing class is extended to define a new class, the existing class is often called the superclass and the new class is often called the subclass.
The subclass inherits all of the variables and all of the methods defined in the superclass and its superclasses.
The behavior of methods inherited into the subclass may or may not be appropriate for an object instantiated from the subclass. If they are not appropriate, you can change that behavior by overriding the methods in the definition of the subclass (provided that they were originally declared virtual in the superclass).
To override a method in the subclass, reproduce the name, argument list, and return type of the original method in a new method definition in the subclass.
Provide a body for the overridden method, causing the behavior of the overridden method to be appropriate for an object of the subclass.
Any method that is declared virtual can be overridden in a subclass.
The program discussed in this lesson extends a Radio class to produce a subclass that simulates an upgraded car radio containing a tape player. Method overriding is used to modify the behavior of an inherited method named playStation to cause that method to behave appropriately when a tape has been inserted into the radio. This is often referred to as runtime polymorphism
Method overriding is different from method overloading. Method overloading will be discussed in the next lesson.
In the next lesson, I will explain the use of overloaded methods for the purpose of achieving compile-time polymorphism.
A complete listing of the program is shown in Listing 8 below.
The primary difference between this program and the program in the earlier
lesson (whose objects simulate car radios) is the inclusion in this
program of a new class named Combo. The class named Combo
extends the original Radio class to create a new type of radio that
also contains a tape player.
Copyright 2002, Richard G. Baldwin. Reproduction in whole or in part in any form or medium without express written permission from Richard Baldwin is prohibited.
Richard Baldwin is a college professor (at Austin Community College in Austin, Texas) and private consultant whose primary focus is a combination of Java, C#, and XML. In addition to the many platform and/or language independent benefits of Java and C# applications, he believes that a combination of Java, C#, and XML will become the primary driving force in the delivery of structured information on the Web.
Richard has participated in numerous consulting projects and he frequently provides onsite training at the high-tech companies located in and around Austin, Texas. He is the author of Baldwin's Programming Tutorials, which has gained a worldwide following among experienced and aspiring programmers. He has also published articles in JavaPro magazine.
Richard holds an MSEE degree from Southern Methodist University and has many years of experience in the application of computer technology to real-world problems.
© 1996, 1997, 1998, 1999, 2000, 2001, 2002 Richard G. Baldwin