Designed and Written by Michael Bartlett
We have now reached the most important part of the C# Fundamentals course - the Object-Oriented tutorial. This tutorial contains 4 code samples which will demonstrate the various ways C# implements OO principles. Fortunately, the last three tutorials have actually done a lot of the groundwork for us. You can begin this tutorial being quite comfortable with the idea of classes, objects, methods, static methods and object constructors.
Let's get stuck in to listing 4x1. The first thing you'll notice is the text: using kill = System.GC. I've put this in to demonstrate a technique called aliasing which allows you to import any namespace into your program and use whatever alias for it you want. Later in listing 4x1 I use the method System.GC.RequestFinalizeOnShutdown(). This, however, is a rather long statement, so by aliasing System.GC (GC stands for Garbage Collector) with the name word kill I actually only need to write Kill.RequestFinalizeOnShutdown(). This technique can be useful if you are working in a large programming team where namespaces exist that are incredibly long-winded and maybe do not describe the functionality in the way you would like. For the record, RequestFinalizeOnShutdown() forces all class destructors to run when the Main method exits. Destructors are not like C++ destructors. In C# a destructor is only called when an object is reclaimed by the garbade collector. If you have any clean-up processing that needs to be executed when you are finished with an object, you should explicitly code that as a method which can be called by other programmers who might use that class. Microsoft recommend that you use the method name Close() if the object can be opened again later, or Dispose() if the object won't. In listing 4x1 I have executed the RequestFinalizeOnShutdown() method and implemented destructors (which can be spotted in the code by a tild proceeding a class name) so you can see the garbage collector in action.
Aliasing, consts and readonly identifiers (taken from listing 4x1.cs)![]() |
The next thing you'll need to know about classes, is that all class variables are set to sensible values by C# if they are not set by the programmer. The default values for primitive types are:
Type | Default Value |
Numeric | 0 |
Bool | false |
Char | '/0' |
Enum | 0 |
Reference | null |
In the code snippet, numberOfInstances is therefore set by C# to 0. This is a static value that is incremented every time a constructor is called for the class ASimpleClass. This means the value will always represent the number of object instances active. You should also have noticed the const and readonly modifiers. A const value cannot be changed. A readonly value can be changed once and once only. The reason for this is that some values might not be known at compile time, but once known at runtime should not be changed. In C# object data handling can be managed through via the use of properties. Consider the following code:
Code that properties are designed to replace (taken from listing 4x1.cs)![]() |
This is pretty standard OO code. Two methods will typically be defined; one for getting the value, and one for setting the value. C# replaces these types of methods with properties as shown in the code below. Before we discuss properties there is something else that needs to be quickly addressed. In the code example note that method ChangeX pulls in a parameter called x. Also note that there is a class data member called x. How do we differentiate? We use the this keyword. this is way of referring to the current instance of the class. A lot has been said about this keyword, but that's all there is to it really!
Properties in action (taken from listing 4x1.cs)![]() |
As you can see from the code above, properties are pretty simple to set up. Firstly you need to define a name for your property, which will map directly to a class data variable. Then you decide whether or not you are going to implement both the get and set methods, or just one. Then you implement them in the way shown. You can perform whatever calculations you wish, but in the code snippet I have just done a pretty standard get and set. The set is accomplished using the C# keyword value which refers to whatever has been passed in by the method caller. get is achieved using the C# keyword return. Also note from listing 4x1 that properties that refer to static data members must also be declared as static. Once a property has been set up, you can access it as if was a normal class variable by writing the class name, followed by a full stop, followed by the property name.
As well as the property defined above, ASimpleClass also has had a static property set to map to its static data member, numberOfInstances. Listing 4x1 then contains the class that holds the Main method. This class, GettingStuckIntoClasses, simply creates a number of instances of the ASimpleClass class, ensuring it gets and sets their properties in order to demonstrate everything we have covered so far. Executing the program results in the following output:
>4x1![]() |
Listing 4x2 contains 3 classes. The first class, InheritFromMe, contains a single property which maps to a data member called a_value. This class has been designed specifically so that another class may inherit its methods and data. There are three methods in this class which should interest the reader. These are shown in the code below. You may have noticed the virtual keyword. This is a way for the author of the class to allow other programmers to write their own implementations of these methods in any classes that they derive from this one. Of the three methods, two are stated as being virtual, but one - NormalMethod - is not. There is a reason for this, but we are not concerned about this just yet; all shall be revealed eventually.
Virtual and non-virtual functions (taken from listing 4x2.cs)![]() |
The second class in listing 4x2 is called Inherits and not surprisingly is the class that inherits from our first class, InheritFromMe. Inheritance is achieved when the class is declared by simply placing a colon after the class name, followed by the class that it will inherit from:
Syntax for achieving inheritance (taken from listing 4x2.cs)![]() |
Once this is done, extra data members and methods can be added. Because the class we are deriving from is declared as public, we have full access to everything. We also can now override any virtual functions if we so wish by using the override keyword as shown in the code below. If we want to override a method that was not declared as virtual, we can do so using the new operator, as with the NormalMethod function shown in listing 4x2; this technique is known as hiidng. Also note that we are only overriding the first virtual function in our derived class, but not the second - we are happy with the base class implementation for now.
Overriding and Hiding (taken from listing 4x2.cs)![]() |
The final class in listing 4x2 is the class GettingStuckIntoClasses2, which contains the Main entry point and will create a few instances of the classes I've already shown you to demonstrate polymorphism and inheritance. The first thing it does is set up two instances of the classes, naming them baseInstance and derivedClass. baseInstance is an instance of InheritFromMe and derivedClass is an instance of Inherits. The program then calls the two Virtual functions to demonstrate that derivedClass is indeed overriding the first virtual function.
Next up, demonstrateBaseCall is called on derivedClass. We haven't covered the contents of this method yet, but it is pretty simple; the method uses the base keyword to invoke methods of its base class. If you are still unsure, check the code and then run the program and it will make perfect sense.
Polymorphism is demonstrated in its full beauty at this point in the program:
Polymorphism (taken from listing 4x2.cs)![]() |
This is because even though I am using references of the base class type, C# still calls the appropriate methods based on the actual type that the reference is pointing to. This is superb because one doesn't need to know the actual exact types being pointed to. All that is important is that the references are of a class that is an ancestor of the types being pointed to. This means that you could create a class called animal, and then inherit from this class to create dogs, cats, anteaters, elephants, etc. If you then wrote a virtual function called eat you could override how it worked for each different animal (cats and dogs use jaws, but anteaters suck). Then you could simply store all your animals in an array of type animal and simply tell the lot of them to eat. Polymorphism would kick in and make sure each one ate in the appropriate way.
If you recall the non-virtual method NormalMethod which we ended up having to hide, you'll probbaly be wondering how that fits into all of this. Well, the fact is that the polymorphic advantages of using and overriding virtual methods do not exist with non-virtual methods that are hidden. Below we try and do the same with our objects using NormalMethod as we just did using VirtualFunction:
Attempting Polymorphism through Method Hiding (taken from listing 4x2.cs)![]() |
Running the program, however, shows that hiding methods does not utilise polymorphism; we have a base reference and a derived reference both pointing at derived instances. If polymorphism was in play we would be alerted that NormalMethod had been called twice in a derived context, but as you can see, it hasn't. Note how the base constructors are being called, followed by the derived class constructors.
>4x2![]() |
Listings 4x3 and 4x4 demonstrate Protected access, Sealed classes and Abstract classes. The code example in 4x3 is an extension of 4x2 (most of it is the same) but protected access is demonstrated through a password example. Protected access is simply an access level that prohibits everything outside a class's code body and its derived class's code bodies from accessing the class's protected elements. Listing 4x4 shows Abstract classes and Sealed Classes, which are direct opposites of each other. Abstract classes mandate inheritance, while Sealed classes prohibit it. The annotation in the code should be more than ample to explain in detail how these concepts work.
[OVERVIEW] | [TUTORIAL 1] | [TUTORIAL 2] | [TUTORIAL 3] | [TUTORIAL 4] |