Library tutorials & articles

A Twisted Look at Object Oriented Programming in C#

C++ and Java Gumption Traps

It's time for another break from theory and on to some real world programming challenges. In this chapter, you take a detour into the world of gumption traps. A gumption trap is a situation that is so frustrating that it sucks the "gumption" or energy out of you. Surprisingly, a lot of gumption traps come from preconceived notions of what "should" be correct. C++ and Java programmers are prone to gumption traps due to their assumptions about the C# language. In other words, C++ and Java programmers have a bit of "language baggage" that needs to be left behind. So, if you have a language neurosis, this chapter is for you.

Top Ten C++ Gumption Traps

In no particular order.

1) Arrays in C# are Objects

In C#, arrays are objects derived from System.Array. The following code snippet creates an array object that can contain ten elements of MyClass:

MyClass[] arrayMC= new MyClass[10];

What may surprise you, is that this call does not create any instances of MyClass! In fact, if you iterate over the array, you will find that the array contains ten null elements. To fill the array, you must explicitly create ten instances of MyClass and fill the array:

MyClass[] arrayMC= new MyClass[10];
[STAThread]
static void Main(string[] args)
{
    //
    // TODO: Add code to start application here
    //
    Class1 c1= new Class1();
    for (int i=0; i<10; i++)
    {
        c1.arrayMC[i]= new MyClass();
    }
    Console.ReadLine();
}

2) Declaring an Object Simply Creates an Un-initialized Object Variable

In C++, when you declare an object, the object is created. Not so in C#. The following code simply declares an un-initialized reference variable of Type MyClass. The variable occupies memory on the stack to hold an address. No object is created on the heap. If you try to use c, the compiler will complain that the variable has not been initialized.

MyClass c;

The following code snippet, creates a variable and initializes the variable to null, so that the reference does not refer to any object. If you try to use the reference variable to touch a field or method of the class, the code will throw a null reference exception.

MyClass c= null;

So, if you want to declare and initialize a reference variable and create an object in C# you must use the new keyword like this:

MyClass c= new MyClass();

"c" is now a reference variable of Type MyClass. If this is a local variable, then "c" uses memory on the stack which contains the address of an object of Class MyClass on the heap.

As discussed earlier, the string class (strings are immutable and shared) is an exception to the rule. You can create a string like this:

string s= "Hello";

3) C# Uses Automatic Garbage Collection, Destructors are not Deterministic

The call to new may bother a C++ programmer who may now feel obligated to call delete appropriately. All I can say is lighten up! Objects are (mostly) reclaimed automatically in C#. In a nutshell, when available memory falls, the garbage collector is called, freeing up any objects that are unreachable. If you are done with an object, you can assist the garbage collector by releasing all references to the object. Even if two objects contain references to each other (circular references), the objects can still be collected if they become "unreachable."

Since objects in C# are garbage collected, it is not a good idea to reclaim resources in the destructor. A destructor will not be called until the object is reclaimed by the garbage collector. In other words, destructors are not deterministic in C#. Critical external resources are best released by inheriting from IDisposable and implementing the Dispose method, or by using C#'s try, catch, finally construct to insure that any allocated resources are reclaimed in the finally clause. Here is a twisted example of try, catch, finally in C#:

try
{
    OpenToiletLid();
    Flush();
}
catch(OverflowException e)
{
    CallPlumber();
}
finally
{
    CloseToiletLid();
}

4) The Assignment Operator Does Not Call the Copy Constructor

This one really confuses a lot of coders. In C# the assignment operator simply assigns a reference variable to an existing object, to a new object, or to null. After the assignment, the reference variable contains a reference to an object or to no object (is null). The assignment operator does not call the copy constructor to create a new object. It is quite legal in C# to have two reference variables that contain references to the same object. The two variables occupy different memory locations on the stack, but contain values that point to the same memory address on the heap. Both references would have to be released (e.g. the reference variables go out of scope, be reassigned, set to null) before the object can be reclaimed. As a result, the "destructor" will only be called once. The following code simply creates two references to the same object:

MyClass c1= new MyClass();
MyClass c2;
c2= c1;
bool isSameReference= (c1 == c2); // c1 and c2 contain references to the same object on the heap
Console.WriteLine(isSameReference.ToString()); // output--> true

Be clear that if variable c1 contains the only reference to an object, setting c1 to null or reassigning c1 to another object will make the original object unreachable and eligible for garbage collection.

5) Values and References to Objects are Passed By Value

By default, objects in C# are not passed by reference. (Unlike Java, C# does support passing by reference using the ref keyword.) In C#, you pass a reference or a value to a method. You cannot pass an object to a method. By default, all calls to methods are by value. Now is that clear! In other words, you pass a reference to an object to a method, not the object itself. The reference is passed by value so that the a copy of the reference goes on the stack. The key here is that the object is not copied onto the stack and you can touch the object while inside the method. If you want to truly pass an object by reference (for a swap routine) use the ref keyword. Remember, you cannot pass an object, so you are actually passing a reference by reference. Oh, I have a headache.

6) Const is Hard Coded Into the Caller and not Version-able

I find this one a bit weird. A const field value is hard coded into the caller for optimization so that the value is not updated until you recompile the caller. If you want to version-able read only field declare it readonly. Finally, you can provide a get only Property like this:

public string ModelName
{
    get
    {
        return modelName; // danger, return ModelName --> stack overflow!
    }
}

7) Supports Single Inheritance of Implementation and Multiple Inheritance of Interfaces

C# does not support multiple inheritance of implementation. It does support multiple inheritance of interfaces (pure virtual classes) and single inheritance of implementation.

8) Program to Return Single Values

Although C# does support an out parameter, in general, C# programs are designed to return single values. Consider redesigning your application or returning immutable objects.

9) Strings are Immutable

The string class is a very special class. First strings are immutable. Every time you concatenate a string, you may be creating a new string object. If you want a mutable class, use StringBuilder. Second, you can create a string object using the assignment operator. Third, strings are shared so that two strings that you create may occupy the same space in memory. Fourth, the equality operator is overloaded for string and checks for content equivalence, not "reference based equality."

Note: The overloaded string equality operator only works on reference variables of Type string. Be careful! Thanks to Jon S. for this insight.

object a= "Hello";
object b= "Hello";
bool isSameReference= (a == b); // test if a and b contain references to the same object or to no object (are both null)
bool isSameContent= ((string)a == (string)b); // test if string referenced by a and string referenced b have the same content/value or a and b are both null

Session["KEY"]="VALUE"; // Error! The left hand operand is of type object! This is a reference based comparison. Do this:
(String)Session["KEY"]="VALUE"; // content equivalence

10) bool is a Value Type

C# has a built in type for Boolean which can have a value true or false. The default value of bool is false. Enough said.

Top Ten Java Gumption Traps

In no particular order.

1) No Checked Exceptions

The compiler in C# will not enforce catching of any checked exceptions. So there is no support for declaring a checked exception (no throws keyword).

2) All Methods are Not Virtual By Default. All Members are Private by Default.

In Java, all methods are virtual by default and can be overridden. Not so in C#. You must explicitly declare a method virtual if you want it to allow it to be overridden. You also cannot implicitly override or hide a virtual method, but must explicitly use the override or new keyword. In Java, members have package level access by default. In C# all members are private by default.

3) Const, ReadOnly and Get Only

I find this one a bit weird. A const field value is hard coded into the caller for optimization so that the value is not updated until you recompile the caller. If you want to version-able read only field declare it readonly. Finally, you can provide a get only Property like this:

public string ModelName
{
    get
    {
        return modelName; // danger, return ModelName --> stack overflow!
    }
}

4) All Types in the NET Framework Derive from Object

C# has a unified type system so that all reference and value types derive from Object. For instance, int is an alias for System.Int32. As a result, you can call ToString() on any value or reference type. This simplifies debugging in the Console mode.

5) The Visual Studio IDE Startup Object is Empty By Default

If the startup object string is empty and you declare more than one "Main" method in a Visual Studio project, the project will not compile. This is very frustrating. Trying to set the startup object property in the Visual Studio IDE is non-trivial. Let me save you some pain. To set the startup object, select View --> Solution Explorer. In the Solution Explorer window select the name of the project. Now select View --> Property Pages. (Do not select the Property Window!) Alternatively, right click on the project name and select Properties. Now select the Common Properties folder in the left window. You can now select the appropriate startup object from the drop down list. Don't forget to click on "Apply."

6) Method Names Start with Upper Case

This one is pretty self explanatory.

7) Getters and Setters are Implemented Using Properties, Which are Upper Case

C# formally supports the concept of getters and setters with Properties. Note that properties are upper case by convention. Here are the setter and getter methods as properties:

// Properties
public int SecondsToToast
{
    get {return secondsToToast;} // danger, return SecondsToToast --> stack overflow!
    set
    {
        if (value > 0)
        {
            secondsToToast= value;
        }
        else
        {
            secondsToToast= 0;
        }
    }
}

The reserved word value is used to represent the caller’s input. Note the syntax used to call a property. To set a property use:

t.SecondsToToast= 12;

8) Use "is" instead of "instanceof", ArrayList instead of ArrayList/Vector, StringBuilder instead of StringBuffer, Hashtable instead of HashMap/Hashtable, System.Environment.NewLine instead of line.separator..

Just trying to save you some headaches.

9) There is an "enum"

Java evolved before the concept of type safe enum. C# has built in support for type safe enums like this:

public enum ColorType {Black, Red, Yellow, White};
private static ColorType DEFAULT_COLOR= ColorType.Black;

You can still create a custom enum in C# like this:

sealed class MyEnum
{
    private String name;
    private static int nextOrdinal= 1;
    private int ordinal= nextOrdinal++;
    private MyEnum(String name)
    {
        this.name= name;
    }
    public override String ToString()
    {
        return name;
    }
    public int ToOrdinal()
    {
        return ordinal;
    }
    public static MyEnum INVALID= new MyEnum("Invalid"); // ordinal 1
    public static MyEnum OPENED= new MyEnum("Opened"); // ordinal 2
    public static MyEnum CLOSED=new MyEnum("Closed"); // ordinal 3
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main(string[] args)
    {
        //
        // TODO: Add code to start application here
        //
        Console.WriteLine(MyEnum.OPENED.ToString());
        Console.WriteLine(MyEnum.OPENED.ToOrdinal().ToString());
        Console.WriteLine(MyEnum.INVALID.ToString());
        Console.WriteLine(MyEnum.INVALID.ToOrdinal().ToString());
        Console.WriteLine(MyEnum.CLOSED.ToString());
        Console.WriteLine(MyEnum.CLOSED.ToOrdinal().ToString());
        Console.ReadLine();
    }
}

10) String Equality Operator is Overridden for Content Comparison

Thankfully, the string equality operator in C# has been overloaded and checks for content/value equivalence. One less gotcha for the C# novice.

Note: The overloaded string equality operator only works on reference variables of Type string. Be careful! Thanks to Jon S. for this insight.

object a= "Hello";
object b= "Hello";
bool isSameReference= (a == b); // test if a and b contain references to the same object or to no object (are both null)
bool isSameContent= ((string)a == (string)b); // test if string referenced by a and string referenced b have the same content/value or a and b are both null

Session["KEY"]="VALUE"; // Error! The left hand operand is of type object! This is a reference based comparison. Do this:
(String)Session["KEY"]="VALUE"; // content equivalence

OK, boys and girls. It's not your father's language. Get over it<g>.

The Equality Operator Revisited

Thanks to Jon S. I have revisited the concept of equality on operands of the reference type. The equality operator, in general, compares operands by reference. The string equality operator is overloaded to compare operands by value. In plain English, the equality operator normally checks to see if two object variables refer to the same object on the heap. The string equality operator is overloaded to check for content or value equivalence.

In not so plain English, the equality operator normally checks to see if two reference variables contain references to the same object on the heap. In C# local reference variables go on the stack and contain the address of the object on the heap. So local reference based equality in C# checks to see if two reference variables, stored on the stack, contain addresses which are equal. In other words, it checks for address equality. This is referred to as "reference based" equality or "compare by reference" or "referential equality". Whew!

As previously stated, the string equality operator is overloaded to check for value or content equivalence. In not so plain English, the string equality operator compares the content or value of string objects on the heap. It does this by obtaining the address of the objects from the values of the reference variables stored on the stack.

Now I would like to simplify this discussion to that of "object" equality vs. "content" equality, but I cannot! By convention, object equality refers to content equality! I would also like to reduce this discussion to referential "equality" vs. content "equivalence", but I cannot. Unfortunately, by convention, the Equals() method is used to determine content equivalence!

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