Library tutorials & articles

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.

Comments

  1. 18 Feb 2009 at 11:33
    That seems to be a good article...But I have a serious question... For example : to draw a sqare we need two points. and to draw a circle we need a point and a radius. How can we use interface and at the same time ;handle this situation . I think it cannot be done...Or if it can be done please let us know.
  2. 09 Jan 2007 at 10:08

    thanks for the useful info

    regards

     

  3. 18 Jul 2006 at 15:51

    If you have tried developing large extensible business applications where data needs are in the gigabytes if not terabytes in any static OO based programming language, then you are in for a lot of headaches. OOP does not lend itself well to this type of development as so called real world objects do not translate well into OO objects, most so called real world objects in the business world translate to data. This fact is lost on many managers, developers and analysts. Most applications in the real world are business related and not some academic project. I have been privy to many failed projects because of the selection of high level development language invariably they have always been statically typed. The one overlooked programming paradigm that has been neglected because of the OO hype is table or data driven programming. This form lends itself well to business related applications and can be used in conjunction with any programming paradigm but work wonders with a dynamically type languages. It is easily extensible, scalable and can be easily modified without throwing hundreds of users of the network.

    As for Interfaces, function overloading, generics, templates, reflection, RTTI, default parameters, adding scripting etc, these are kludges to make a static language do what a dynamic language already does, and done badly at that.

  4. 19 Jan 2006 at 17:09

    When I first learned OO programming I was primarily excited by code re-use on the object side of the equation. Objects considered as providers of services to other code that acts as a client.


    After many years of developing OO application I eventually grew to see the vast web of code dependencies and to realize that every piece of client code is itself serving some other code i.e. tier 1 calls tier 2 calls tier 3 (tier 2 code is being served by tier 3 but is serving tier 1). I realized that my objects provided a great deal of code re-use but what about all the code that used those objects? That is where Interfaces come in. Thanks to an interface all of your code that uses object A can be swapped out to use object B, C, D, or E regardless of the object type and therefore ancestry. Regardless of object implementation.


    In another sense, Interfaces are more "fair". If an object can perform the services declared in the interface then it can be used by code that uses the interface. This means no strict binding to particular object hierarchies and that can save a lot of headaches.

  5. 22 Mar 2004 at 10:56
    Moving from VB toC#? Looking for a lot of practical explanations of OO topics? Read this article.  Really good OO 'shot gun' intro to using C# with OO methodology.  This format should be used by publishers like O'Reilly to create a nice tidy OO break down, in a book.  This kind of material, to the point, all in one spot, explanation is sadly missing from most OO explanations.  The worst part about OO is buying into it.  If you come from a non OO or just didn't really use OO background, like I did, your probably looking for the 'why' as much as the 'how'.  This article, in what has to be said is few pages, really helped me see the worth and implementation of OO in C#.  I think that that is because the author uses the whole development approach to explain topics.  Meaning, not only is the raw abilities, the 'how' of OO shown (e.g like inheritance) but the author uses patterns to show the 'why'.  This article finally flicked the old proverbial light switch for me.  Obviously there is much more to OO than just how to program with a language that supports OO.  For me the missing link, was the connection with patterns.

    Read the article even if you work with OO, give it to people that are just starting OO.  

    I'd really like to see this article as the core of explaining more OO topics, in the same format.  
  6. 04 Mar 2004 at 12:54
    Tried doing print version, when i printed, out came tons of blank pages.

    Workaround:
    Save printable page as complete web page.

    Edit default.css file in _files subfolder of saved page.

    Change the CODE.Pre line to:

    CODE.pre {
       PADDING-RIGHT: 7pt; PADDING-LEFT: 7pt; WIDTH: 100%; COLOR: #000000; PADDING-TOP: 7pt; BACKGROUND-COLOR: #fbedbb
    }

    (removes the display:block)
  7. 20 Dec 2003 at 16:07

    RRIOS:
    Thanks for the excellent write up on this concept. It has actually helped quite a bit to give me a better perspective on the subject of Interfaces. I particularly liked your analogy of Royal family vs democracy.
    Thanks!

  8. 19 Dec 2003 at 14:37

    Inheritance is rigid. It´s like being born into the royal family. Sure, you get alot of privileges without doing anything, but you carry all the baggage and stigmas that come with your family name.  You can´t JOIN the royal family. You have to be born into it.


    Interfaces are democratic. A system that declares an interface is saying "I dont care who you are or how big or small you are: if you agree to these rules you may join the club". Think of your drivers license. The DVM declares an interface and ANYONE can use the road system as long as they stick to the rules (implement the interface).


    In programming terms, suppose you are using two systems, in one you have created a Vehicle class and have several child classes like Car, Bicycle, and Bus. In the other you have cooking items, say for a recipe system. You may have classes like Flour, Eggs, Carrot. These two groups of classes have nothing to do with each other.
    You create a third point of sale system for a retail store, and you plan to sell among other things, bicycles and flour. Your point of sale system could declare an IRetail interface that defines to other classes what they need to implement in order to participate in the Point of Sales system like Price(), TaxKey() and UPC().
    This means you can reuse your Bicycle and Flour classes, just by adding the implementation of the IRetail interface in order to use them in the Point of Sale system. This new system will be capable of handling any kind of object as long as it implements IRetail.


    So Interfaces are an advantage to both the system that declares the interface (they dont care what class of object they are handling)  and to the classes that implement them (they can participate in any system as long as they agree to the rules).

  9. 03 Dec 2003 at 06:22
  10. 11 Nov 2003 at 16:22

    An interface allows a developer, but more specifically a designer, to say "These features must be implemented." It is then up to the developer to work out the details of how to deliver on those features/abilities. It creates a boundary of expectation from other users of the object.


    They can say "Oh I i see it implements IInterface, therefore it will definitely have X, Y and Z". It is quite useful when you wnat to implement concepts in multiple objects instead of concrete functionality. For instance, say you had two types of collections one was a tree and one was a jagged array of parent/child IDs (which in many ways is a tree represented another way but they are syntactically different). If you wanted to implement something like "GetParent", you couldn't (well maybe you could, but go with me for the sake of example) implement one generic function for "GetParent" that would work for both the Jagged Array and the Tree. YOu could however have both objects implement an interface, and then have the developer do the syntactic details of returning an items parent.


    Anyway, more half baked analogies.

  11. 05 Nov 2003 at 16:26
    Very good analogy about interfaces. But ......why do we need them?
    One of the questions that still persists for me, (and something I have never seen a discussion on) is what real benefit Interfaces offer; it seems they offer some, but that it is somewhat limited. Let me give an analogy, in the form of an old joke. An engineer, a phycicist and an economist were on a desert island with a can of pineapple that had survived the boat wreck. Both the engineer and the physicist made multiple attempts using their scientific knowledge to open the can of pineapple, but to no avail. After some time the economist remarked: "This shouldn't be a difficult problem to solve." "Well, tell us", replied the engineer and physicist in unison. Said the economist, " Assume a can opener."
    This is what economists do. They make assumptions and think they have contributed something. Is this not like Interfaces, whose job, it appears, is to make policy statements without ever getting around to implementing anything? All the same amount of coding has to be done whether the interface is used or not; in fact there may be considerable duplication of code if minor changes to an implementation are needed. So what has been achieved? Not nearly as much, it seems, as inheritance would offer, were full inheritance of pure methods possible. This is perhaps why an Abstract class seems to be more useful.

    I would appreciate anyone who can resolve my perplexity here. I could learn something.
  12. 17 Oct 2003 at 21:41

    Pretty good article.  Its the first time i acutally laughed and smiled while reading your article.  Very good read..


    There are a few inaccuracies though just to point out...


    1.


    MyClass c;
    c.someMethod()


    does not throw a null reference exception. It actually throws a compiletime error "Use of unassigned local variable".  In c# variables much be assigned before use or something like that..


    2.
    array[] = new array[10]
    only returns an array of null references if it you are declaring an array of ref type.  If it is a value type, the values will be assigned their default values..


    and 3.
    comparing type compatibility in most cases should use "as" instead of "is" because if you use is before casting the type, you in effect check the type twice, once before the cast, and once during the cast..
    Its just an optimisation really..


    Other than that, like i said before, best read i have ever had.  Learned a few new words too like "gumption".  Who would have thought..

  13. 01 Oct 2003 at 16:59

    This intro to C-Sharp is a very good tutorial, but its only an intro. The naming of variables and methods need to be standardized for easier readability. It also does not cover data driven dynamic web apps.

  14. 29 Aug 2003 at 05:32

    Quote:
    What a shame the author never learnt to use a sensible standard for naming variables.


    ...it is a good article and Microsoft does advocate clearer naming of Object variables.


    It's a shame you never LEARNED proper grammer.

  15. 13 Aug 2003 at 23:36
    This is a seriously good intro article on OOP. What a shame the author never learnt to use a sensible standard for naming variables. Names like P, M, N, and at one stage an 'i' to represent (according to my school) 'dblInterest' just tend to obscure code and (I guess) put off less persistent student code nerds than myself.



  16. 01 Jan 1999 at 00:00

    This thread is for discussions of A Twisted Look at Object Oriented Programming in C#.

Leave a comment

Sign in or Join us (it's free).

Jeff Louie

Related podcasts

  • Object-Oriented Programming in Ruby

    In this episode, I talk with Scott Bellware about object-oriented programming in Ruby, and Ruby's object model. This is taken from a private conversation, and the audio quality suffers at times. Much thanks to Scott for allowing this to be released.This episode of the Alt.NET Podcast is bro...

We'd love to hear what you think! Submit ideas or give us feedback