A Twisted Look at Object Oriented Programming in C#

Where is My Inheritance?

In this chapter you will learn how the fictional character Phaedrus explains the complex subject of a class hierarchy in "Zen and the Art of Motorcycle Maintenance", Robert M. Pirsig, Quill, 1974, 418 pp. You will also learn the art of "black box programming" with abstract classes and abstract methods.

Lets Get Abstract

One of the major advantages of OOP is that is simplifies the task of creating and maintaining complex applications. The road to this simple life is through "abstraction". Abstraction is the ability to "decompose" a complex problem or structure into simpler problems or pieces. In abstraction, you try to find the "essence" of a thing or idea. You break down a complex problem into smaller, more manageable chunks. Abstraction is what the character Phaedrus describes in Chapter 8 in "Zen and the Art of Motorcycle Maintenance."

"John looks at the motorcycle and he sees steel in various shapes and has negative feelings about theses steel shapes and turns off the whole thing. I look at the shapes of the steel now and I see ideas. He thinks I am working on parts. I'm working on concepts."

Phaedrus then proceeds to divide up the concept of a motorcycle into components and functions. Then he divides the components into a power assembly and a running assembly. To which he concludes:

"Finally you see that while I was splitting the cycle up into finer and finer pieces, I was also building a structure. This structure of concepts is formally called a hierarchy and since ancient times has been a basic structure for all Western knowledge.... That's all the motorcycle is, a system of concepts worked out in steel."

Thus the fine art of motorcycle maintenance is based on abstraction, decomposing the complex concept of the motorcycle into a hierarchy of smaller simpler concepts. The fine art of OOP is also based on abstraction. You decompose the complex application domain into a hierarchy of smaller pieces that make the application easier to create and maintain. Now I am not saying that Phaedrus was a great object oriented programmer, but he clearly understood the importance of critically examining the problem at hand and dividing up the problem into a hierarchy of ideas.

Let's look at Phaedrus' motorcycle in C#. First, a motorcycle is a vehicle since it can carry a payload. Second, it is motorized since it has a motor and consumes fuel. The "essence" of a motorcycle is that of a "vehicle" that is "motorized". Of course a motorcycle differs from a a car, an airplane, or a boat. Motorized vehicles can be ridden, driven, flown or piloted. Land vehicles can have two wheels or four wheels. What we have here is a hierarchy of ideas. We go from a most general concept to a more specific or "specialized" concept. Just as Phaedrus' abstraction of the motorcycle made him a better mechanic, your abstraction of a complex application, makes you a better and more efficient programmer. You do not think of a program as concrete parts (global data and methods). You think of a program as a hierarchy of ideas. Welcome to "Zen and the Art of Application Maintenance."

Inheritance Is the Means to Abstraction

Let's face it. Just creating a hierarchy of ideas is not enough. The language must provide a mechanism to support the abstraction hierarchy. That mechanism is called inheritance. Inheritance allows programmers to modify or extend (or inherit) the properties and behavior (methods) of a class. It promotes "code reuse", the holy grail of programming. Throughout the .NET framework, you see the use of abstraction and class hierarchies.

Inheritance in the .NET Framework

All classes in the .NET framework derive from a single base class called Object. The Object class is the "root of the type hierarchy." Even the built in value types are aliases to classes that derive from Object, so that int actually maps to System.Int32, which derives from Object. As a result, you can call the Object class methods Equals, Finalize, GetHashCode and ToString on any class in the .NET framework. This makes it very easy to debug an application using console output since any .NET class will implement the ToString() method.

An example of inheritance in the ASP.NET framework is a TextBox. Here is the class hierarchy for System.Web.UI.WebControls.TextBox:

System.Object
  System.Web.UI.Control
    System.Web.UI.WebControls.WebControl
      System.Web.UI.WebControls.TextBox

The TextBox control inherits from WebControl which inherits from Control which inherits from Object. Wow. And I thought my family tree was complicated.

Getting Your Inheritance

In the following section you, a twisted programmer, build a "SpaceToaster" class in C# that inherits from the Toaster class.

Disclaimer: This is a very twisted tutorial. Reading this online book may be hazardous to your mental health.

You have been contracted by a famous "Boy Genius" movie cartoon character to extend the common household toaster for use as a orbiting satellite transmitter. SpaceToaster is a specialization of a Toaster that extends the behavior of the lowly toaster into a satellite.

Note: For those of you who don't know what cartoon movie I am talking about, either you have no children or you need to get out more. Here's a hint: "Jimmy Neutron Boy Genius", Paramount Movies, Nickelodeon Movies, 2001.

First you need to delete the Main method of the Toaster3 class since an executable can only have one entry point. You can now create a new class "SpaceToaster" that inherits all of the code in the Toaster3 class and has a Main method:

class SpaceToaster : Toaster3
{
    [STAThread]
    static void Main(string[] args)
    {
    }
}

This is not a very useful class! The syntax for inheritance is

class ChildClass : ParentClass {}

Note: Toaster3 is the parent class and SpaceToaster is the child class. You could also say the the child class is a "subclass" or "derived class" and that the parent class is the "superclass" or "base class." The child inherits the knowledge (methods and variables) of the parent class, but can only "touch" the public (and protected) methods and variables of the parent. Sort of reminds me of a trust account. The child's ability to "touch" the assets of the trust is limited.

Next, you can add a new behavior to the derived class extending the functionality of the lowly Toaster:

class SpaceToaster : Toaster3
{
    [STAThread]
    static void Main(string[] args)
    {
        SpaceToaster st= new SpaceToaster();
        Console.WriteLine(st.Broadcast());
        Console.ReadLine();
    }
    public string Broadcast() // <-- new behavior
    {
        return "Hello From Earth!";
    }
}

Which amazingly outputs: "Hello From Earth!"

Since SpaceToaster also inherits the methods and properties of Toaster3, it can still make toast while orbiting planet Earth:

class SpaceToaster : Toaster3
{
    [STAThread]
    static void Main(string[] args)
        {
        SpaceToaster st= new SpaceToaster();
        Console.WriteLine(st.Broadcast());
        st.SecondsToToast= 10;
        Console.WriteLine(st.MakeToast());
        Console.ReadLine();
    }
    public string Broadcast()
    {
        return "Hello From Earth!";
    }
}

Inheritance vs. Containment

One difficult design decision is to decide if a class should inherit from a parent class or hold a reference to a new object. Inheritance represents an IS_A relationship from a generalization to a specialization. Containment represents a HAS_A relationship between the whole and a part. So a car IS_A motorized vehicle, but HAS_A radio. The two relationships can be expressed in code thusly:

class Radio
{
    ...
}
class Vehicle
{
    ...
}
class Car : Vehicle
{
    Radio r= new Radio();
}

The essence of a car is that it is a vehicle. A radio is not essential to a car. Instead a radio is part of a car. That said; let me acknowledge that none of my teenage children agree with me. They are adamant that a radio with a CD player _is_ essential to a car. So a teenage design looks more like:

class TeenageVehicle : Radio
{
    ...
}

Public, Protected and Private

Heck. I forgot that I had promised in Chapter 1 to explain the access modifier "protected". In a nutshell, a private method or variable is only touchable within the class that declared the variable. A public method or variable is touchable from outside the class. A protected method or variable lies between public and private. Protected methods or variables are only touchable (visible) from within the declaring class _or_ from a class that inherits from the declaring class. So declaring a variable protected allows a designer to access your base class variable inside of their derived class. Suffice it to say that there has been a lot of discussion about the use of private versus protected in class design.

Make It Abstract

Is your head spinning yet? If so, I apologize, but I need to introduce one more subject that is related to abstraction, the "abstract class". An abstract class defines zero or more abstract methods. An abstract method defines the signature of the method, but not the body of the method. You cannot create an instance of an abstract class.

Note: Similar in concept to an abstract class is the concept of an "interface". However, an interface contains no implementation, only abstract methods, and is similar to a pure virtual class in C++.

Nothing helps explain a new programming idiom like sample code. Here is an abstract C# class "SpaceToaster" that defines an empty abstract method "BroadcastID()":

abstract class SpaceToaster : Toaster3
{
    public string Broadcast()
    {
        return "Hello From Earth!";
    }
    abstract public string BroadcastID(); // <-- No body or implementation!
}

If you try to create an instance of this abstract class, the compiler will complain:

Cannot create an instance of the abstract class or interface 'JAL.SpaceToaster'

An abstract class is useful in at least four distinct situations.

Defer the Implementation to Another More Knowledgeable Class

First, an abstract class is useful when you know what the class should do, but you don't know how to implement the total solution. In this idiom, you defer the actual implementation or body of one or more methods to another class. A good example of deferred implementation is a sorting class, say a quicksort. You may know how to do a quicksort, but you cannot know the ordinality or ranking of every possible object that needs to be sorted. You may want to write a complex generic sort routine and declare the class abstract with an abstract method Compare(object1, object2). You can even call this abstract Compare method in your Sort class (cool!). Now another programmer with a custom object that needs to be sorted, can inherit from your Sort class and implement the Compare method with code that defines the ordinality of the custom object, reusing your generic quicksort algorithm! In a sense, the child class inherits the incomplete implementation of your Sort class.

Note: C# supports single inheritance of implementation and multiple inheritance of interfaces. In C#, see the IComparable interface.

Formally Hide or Encapsulate the Implementation for Flexibility

Alternatively, you may just want to formally hide or encapsulate the actual implementation of a method. By requiring clients to program to the public view, the abstract method, rather then to the actual implementation, you promote information hiding or encapsulation. This allows you to change the implementation in a future release, without affecting any client calls.

The key concept here is that the implementation can change without affecting a call to your abstract method. This is yet another example of how OOP makes it easier to maintain your code. If you define an abstract method GetData(), the data could come from a database query. To the caller of the abstract method, GetData() is just a black box that returns data. If you later decide to change the concrete implementation of GetData() from a database solution to a text file solution, you can recompile the concrete implementation of GetData() without recompiling classes that call the abstract method GetData().

More interestingly, you could compile an updated class with a different name and load the updated class at runtime (perhaps passing the name of the new class in the Main method). As long as the updated class derives from the same abstract class and as long as the client makes calls through the abstract class, there is no need to recompile the client application. Cool!

Allow Runtime Variation in Implementation

This concept is difficult to grasp at first, but is an extremely powerful idiom. In this idiom, one or more concrete classes inherit from an abstract class, but each child class may provide a different implementation of the the abstract class method or methods. The client does not know what concrete class is going to be requested at runtime, but simply invokes the public view of the method. The actual runtime behavior will depend on which concrete classes is requested at runtime. Thus the method can have more than one form at runtime, which is referred to as polymorphism, to have more than one form.

The classic example is an abstract class Drawable with an abstract method DrawYourself(). Different concrete classes of Drawable items (such as circle or square) implement the DrawYourself() method in a unique manner and inherit from Drawable. You can now create a Hashtable of non-null Drawable objects and iterate over the collection calling someObjectInHashtable.DrawYourself(). Each concrete object in the collection is guaranteed to implement the DrawYourself() method.

Note: Polymorphism implies late binding of a method name to a concrete implementation. In some languages, this magic is done with a v_table, which stores an offset pointer to the actual concrete implementation. OK. That was probably more than you wanted to know.

Mark Thyself

Finally, the abstract class can simply be a marker, allowing you to identify all classes that are based on your empty abstract class like this:

abstract class MyMarker {} // <-- no abstract methods
class Child : MyMarker
{
    static void Main(string[] args)
    {
        Child c= new Child();
        if (c is MyMarker)
        {
            System.Console.WriteLine("Is mine.");
        }
        System.Console.ReadLine();
    }
}

Outputs: "Is mine."

Sample C# Code

We finish this chapter with a bit of sample code that demonstrates the syntax of inheritance from an abstract class. Here again is the abstract version of the SpaceToaster class:

abstract class SpaceToaster : Toaster3
{
    public string Broadcast()
    {
        return "Hello From Earth!";
    }
    abstract public string BroadcastID(); // <-- No body or implementation!
}

This abstract class defines the public outside view or signature of the method "BroadcastID". The method is declared with a return type "string" and an empty parameter list. The declaration says nothing of how the method is implemented.

Any concrete class that inherits from this abstract class, must provide a body or implementation for the function BroadcastID(). Here is an concrete version of a class that inherits from the SpaceToaster class and provides an implementation of the abstract method BroadcastID(). Note the use of the keyword "override" which tells the compiler that you are explicitly implementing the abstract method BroadcastID in your concrete class.

class TwistedSpaceToaster : SpaceToaster
{
    [STAThread]
    static void Main(string[] args)
    {
        TwistedSpaceToaster tst= new TwistedSpaceToaster();
        Console.WriteLine(tst.BroadcastID());
        Console.ReadLine();
    }
    public override string BroadcastID() // <-- concrete implementation
    {
        return "Manufactured By Twisted.";
    }
}

Running this code outputs: "Manufactured By Twisted."

Congratulations at making it this far. All of this theory, however, is giving me a headache. Lets move on to some real world techniques for writing better programs. In the next twisted chapter, we learn a basic design pattern for writing reusable code, the Model-View/Controller pattern.

You might also like...

Comments

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil.” - Donald Knuth