Web Design that taps into the haromny and vision of your dreams.

Introduction to Object-Oriented Programming

By on in Coding

3,252 words, estimated reading time 15 minutes.

Introduction to Programming with C# Series
  1. Introduction to Programming with C# 7
  2. C# Programming Fundamentals
  3. Introduction to Object-Oriented Programming
  4. C# Object-Oriented Programming Part 2
  5. Flow Control and Control Structures in C#
  6. C# Data Types, Variables and Casting
  7. C# Collection Types (Array, List, Dictionary, Hash Table)
  8. C# Operators
  9. Using Data in C# 7 with ADO.Net & Entity Framework
  10. LINQ: .NET Language Integrated Query
  11. Error and Exception Handling in C#
  12. Advanced C# Programming Topics
  13. Reflection in C#
  14. What Are ASP.Net Webforms
  15. Introduction to ASP.Net MVC
  16. Windows Application Development
  17. Assemblies in C#
  18. Working with Resources Files, Culture & Regions
  19. Regular Expressions in C#
  20. Introduction to XML with C#
  21. Complete Guide to File Handling in C#

The object-oriented approach to programming tries to represent abstract or concrete things of the real world in a type of computer code called classes. This article is designed to give you an overview of the concept of Object-Oriented Programming, classes, inheritance and abstraction.

What is Object-Oriented Programming?

Object Oriented Programming is a branch of computer programming which is centred around objects rather than actions, data rather than logic. Objects are modelled after things in the real world or creating data models. By modelling data and things, a set of related properties and actions can be created and interacted with.

Object-Oriented Programming takes the view that the objects we want to manipulate are more important than the logic required to manipulate them.

What is a class?

A class can be thought of as a blueprint for a noun, e.g. a person, place or thing. Noun's have attributes that describe them such as Size, Mass, Temperature, Speed and so on. These attributes are called fields.

A noun can also have actions or processes associated with it. For example, a car could have an action of Accelerate, which would affect the attribute of Speed. These actions are called methods.

A class cannot be used by itself, but it is used to instantiate (create) an instance of an object, which shares all the same fields and methods. Because it is a blueprint, you can create many, many instances, all with the same fields and methods. An instance of a class is called an object. One thing you must keep in mind is that each object that is created from a class is unique; each instance of a class will have its own values for the fields, totally separate from the values of the other objects.

This concept of creating one class that can be reused many times is the foundation of Object Oriented Programming and effective code reuse.

A class should be as specific to the task as possible and only contain nouns dedicated to the task for which the class is to perform.

Why use Classes?

Now, let's have a look at where object-oriented programming really comes into its own. There is a concept known as inheritance where one class can inherit all the methods and fields of a parent, or base, class and also add new functionality, or replace existing methods. This allows a new class to be more focused, targeted towards the object being modelled.

To put that into English with the car example, every car manufacturer uses the same base class. This class defines a basic car with four wheels, a steering wheel, brakes, headlights, seats etc... However, if every manufacturer only used this blueprint, every car produced would be exactly the same - what would be the point?

What we can do is take the functionality from the base class and create another car class and add functionality to it. We can add central locking, water temperature and paint. A different manufacturer may use trim colour and oil temperature and ABS brakes. We can override the methods from the base class so that the basic seats are now heated leather seats, the headlights are HID.

Classes can also use other classes. Our base car class can use a base engine class. Our new car class can override this base engine class to use a super turbo-charged jet-powered engine if we wish.

Classes in C#

A C# application is in essence a collection of classes, structures (shortened to struct) and types.

Firstly, classes and structs are essentially the same, apart from two key differences.

  • Classes are a reference type, structs are value types
  • Classes can use inheritance whereas structs cannot.

Defining a Class

A class is introduced using the class keyword, followed by the name of the class you are creating. Because a class is a block of code, it requires the use of curly brackets (braces).

Let's use the example above and create a Car class. I've called the class called baseCarClass because we will be using this class as a base for new classes in future tutorials.

In the example, we identified several fields and methods associated with a car class.

We are going to create a sample application using the baseCarClass, so create a new console project, and copy the following code into the window.

class baseCarClass
{
}

We can now add in variable declarations for some of the properties of the base car, for example, paint colour, number of seats and direction.

The .Net Framework, nor the compiler are affected by the order in which you declare fields or methods; however, it greatly improves readability if you declare properties at the start of a class, followed by its methods.

class baseCarClass
{
  public string Colour { get; set; }
  public int numberOfSeats { get; set; }
  public int direction { get; set; }
  public string modelName { get; set; }
}

So far all we have done is create a blueprint for a class. We shall now see how to create an actual car object.

A class is a user-defined type so we use the same syntax as declaring an int (for example), but a class also needs initialising with the new keyword. This will instantiate an instance of the class on the heap.

In the Main method of the console application, create an instance of the baseCarClass like this:

static void Main()
{
  baseCarClass myCar = new baseCarClass();
}

We can now set the various attributes of the car.

In your Main method, create an instance of the baseCarClass:

static void Main()
{
  baseCarClass myCar = new baseCarClass();
  myCar.Colour = "blue";
  myCar.numberOfSeats = 4;
  myCar.modelName = "Demo Car MKI";
}

Using the class as a blueprint means that we can create multiple cars with different values for each of the properties.

static void Main()
{
  baseCarClass myCar = new baseCarClass();
  myCar.Colour = "blue";
  myCar.numberOfSeats = 4;
  myCar.modelName = "Demo Car MKI";
 
  baseCarClass myNewCar = new baseCarClass();
  myNewCar.Colour = "yellow";
  myNewCar.numberOfSeats = 2;
  myNewCar.modelName = "Sports Car MKI";
}

Class Methods

We have just created a class with a few properties. We can add a few methods to our classes which will contain certain logic.

class baseCarClass
{
  public bool isEngineRunning { get; set; }
  public decimal speed { get; set; }
  public decimal distanceTraveled { get; set; }
  public decimal fuelLevel { get; set; }
  public Color colour { get; set; }
 
  public void startEngine()
  {
  }
 
  void accelerate()
  {
  }
 
  void brake()
  {
  }
 
  void stopEngine()
  {
  }
}

The compiler is not affected by the order in which you declare fields or methods; however, it greatly improves readability if you declare fields and properties at the start of a class, followed by its methods.

Classes as a Data Type

At this stage, our car does not have an engine, so it's pretty useless. There are many different types of engine that a car can have and we are going to model a base engine as we have created a base car. This way our cars can have a variety of engines.

class baseEngineClass
{
  public string name { get; set; }
  public int numberOfCylinders { get; set; }
  public int MPG { get; set; }
  public int acceleration { get; set; }
}

We can add one class to another in the same way as we add an integer or string. A class is simply another data type.

Let's add the engine to our car class.

class baseCarClass
{
  public string Colour { get; set; }
  public int numberOfSeats { get; set; }
  public int direction { get; set; }
  public string modelName { get; set; }
  public baseEngineClass engine { get; set; }
}

Now all that is left to do is create an engine and give it to a car.

static void Main()
{
  baseCarClass myCar = new baseCarClass();
  myCar.Colour = "blue";
  myCar.numberOfSeats = 4;
  myCar.modelName = "Demo Car MKI";
 
  myCar.engine = new baseEngineClass();
  myCar.engine.name = "Slow Engine"
  myCar.engine.numberOfCylinders  = 4;
  myCar.engine.MPG = 50;
  myCar.engine.acceleration = 2;
}

C# Class Inheritance

Class inheritance is one of the key principles in object-oriented programming. It allows one class to pass on properties and methods to child classes so that they can reuse, extend, and modify existing functionality.

In simplistic terms, class inheritance allows one class to inherit methods, properties and fields from a parent class and to also build upon its functionality. Classes inherit from another class using a colon followed by the class name to inherit from on the class declaration line.

let's create a sports car class which has a folding roof.

public class SportsCar : baseCarClass
{
}

This basic class definition should be familiar, with the additional inheritance " : baseCarClass" bit added on. Basically, this is saying that we want a new class called SportsCar which contains everything that baseCarClass contains.

Next, we can add the folding roof as a boolean property, with two methods, one to raise the roof and the other to lower it.

public class SportsCar : baseCarClass
{
  private bool isRoofUp;
 
  public void RaiseRoof()
  {
    isRoofUp = true;
  }
 
  public void DropRoof()
  {
    isRoofUp = false;
  }
}
SportsCar myCar = new SportsCar();

The SportsCar contains all the properties of the baseCarClass plus those we defined in the SportsCar class.

Using C# Properties

We saw in this example that we created some properties with a get and set keyword after them. These are called properties.

Properties are the same as fields, however, they can execute code when you access them via getters and setters. This allows for protection of class information and enforcement of business rules.

As a general rule, if you mark a field as public, you should convert it to a property. This is because a lot of actions, such as serialisation and deserialisation (we'll cover these later on) only work for properties.

A property can be defined in two ways - there is the automatic property we just saw which is a shorthand version where the compiler automatically generated the backing variable and getter and setter code.

There is also the expanded version which allows you to add in logic and security to the get or set methods. Maybe only certain users can update the value while everyone can read it.

private int _month
 
public int Month
{
  get 
  {
    return _month;
  }
 
  set
  {
    if ((value >= 1) && (value <= 12))  
    {
      _month = value;
    }
  }
}

This will ensure that the value of _month will always be between 1 and 12. If the user of your class passes in a value outside this range it will not be applied.

C# Class Constructors and Destructors

Class constructors and destructors are methods that are automatically called when a class is instantiated or destroyed. They are provided so that the class can be initialized with the correct data, open connections and so on and also to finalise and close connections and files before the object is removed from memory.

You do not need to provide a constructor method in your classes as the compiler will automatically initialize fields to default values using a default constructor which takes no parameters. A constructor should be used when you need non-default values to be initialized. Constructors can also be overloaded to extend the functionality of the method. If you do create an overloaded constructor, the compiler will not provide a default, so if you still require a constructor with no parameters you must provide it yourself.

A constructor is declared as a public method with the same name as the class. In these examples, we are going to continue using the car classes from the previous tutorial. I have added the baseCarClass() constructor to the example, as shown below.

class baseCarClass
{
    public bool isEngineRunning;
    public decimal speed;
    public decimal distanceTraveled;
    public decimal fuelLevel;
    public Color colour;
 
    // Constructor
    public baseCarClass()
    {
    }
 
    public void startEngine()
    {
    }
 
    void accelerate()
    {
    }
 
    void brake()
    {
    }
 
    void stopEngine()
    {
    }
}

When you compile your class, the compiler will automatically add a default "hidden" constructor for you, however, if you want to provide additional functionality then you must do this yourself. When instantiating the class with the new keyword you can pass a parameter to the constructor.

// Constructor
public baseCarClass(Color carColour)
{
    colour = carColor;
}
baseCarClass car = new baseCarClass(Color.Yellow);
//car.colour == Color.Yellow
 
baseCarClass car = new baseCarClass(); // Will not compile
 

As soon as you code a constructor, the compiler will not automatically provide one for you. Only those coded will be present, so if you create a constructor with a parameter you will not be able to create an object with a parameterless constructor unless you write one.

This example constructor will only allow a baseCarClass to be created when a colour is given. The default with no parameters is not available. If you do need it, you will have to create it yourself.

// Default Constructor
public baseCarClass()
{
    // Call the most specific constructor to prevent duplicate code
    baseCarClass(Color.Black);
}
 
// Custom Constructor
public baseCarClass(Color carColour)
{
    colour = carColour;
}
baseCarClass car = new baseCarClass(Color.Yellow);
//car.colour == Color.Yellow
 
baseCarClass car = new baseCarClass(); // Now will compile
//car.colour == Color.Black - default color
 

Constructors and Inheritance

By default all inherited classes will use the constructor as the base class, unless overridden (more on this in a later tutorial).

Because the constructor was added to the baseCarClass, any class inheriting from it can also benefit from having the constructor, thus:

SportsCar sporty = new SportsCar(Color.Pink);
//sporty.colour == Color.Pink

To create a constructor specifically for the SportsCar class we define it in the same way:

// Constructor
public SportsCar(bool roofUp)
{
    isRoofUp = roofUp;
}
SportsCarcar = new SportsCar(true);
//car.isRoofUp == true

What colour is the SportsCar?

Because we have not provided a colour in our constructor, the default constructor for the base class has been called making the colour of the car Color.Black. You can call the constructor of the base class and give it a parameter. This can be done in one of two ways.

Firstly, to provide a default "static" value:

public SportsCar(bool roofUp):base(Color.PeachPuff)
{
    isRoofUp = roofUp;
}

Or you can add another parameter to the constructor and use this to call the base constructor.

public SportsCar(bool roofUp, Color carColour): base(carColour)
{
    isRoofUp = roofUp;
}

Private Constructors

You can also mark the constructor as private, which will have the effect of not allowing the class to be instantiated. This is useful for abstract classes where you should not create an instance.

class PoliceCar
{
  private PoliceCar()
  {
  }
}

The following code will not compile as the program cannot "see" the private method.

PoliceCarcar = new PoliceCar();

Destructors

Destructors work slightly differently in that the syntax is a shortcut to an override of the Finalize method, however, all you really need to know is that they are used to finalise the class ready for removal from memory. The Garbage Collector always calls the destructor; you never call it from within your code. Since you cannot call the destructor, you cannot overload the destructor method.

~baseCarClass ()
{
  // Code to scrap the car (realistically this would be closing files and connections)
}

Method Overloading and Overriding in C#

When inheriting from another class, you may wish to change the default behaviour of a method or create a different method signature. You can do this by overloading the method with your own code.

Method signatures are formed from the parameter list, in particular, the data types. Methods can share the same name within the class as long as the signature is different.

static int Add(int a, int b)
{
  return a + b;
}

In the above example, the signature should be (int, int). In the Visual Studio code editor, if you were to call the Add method after you enter the '(' IntelliSense will pop-up the available parameters. Notice that there is only one option given to you.

Method Overloading

We can add an overloaded method with a different signature and IntelliSense will now present two options, one for each signature of the overloaded method. The compiler finds two methods of the same name and two calls to that method with different parameters. It is able to tell the difference by comparing the signatures of the methods.

static int Add(int a, int b, int c)
{
  return a + b + c;
}

The names of the parameter and the return type have no effect on the method signature, thus the method below will raise an error because a method already exists for Add(int, int), even though it returns a decimal.

static decimal Add(int a, int b, int c)
{
  return a + b + c;
}

Methods should be overloaded when you have similar methods that require different parameters or you want to add new functionality to existing code, however, you should not use overloading to often as it causes headaches during debugging and testing and is more effort to maintain.

Optional Parameters in C#

C# does not have an implementation of optional parameters like those found in PHP, however, you can simulate this feature using method overloading.

static int Add(int a, int b)
{
  return Add(a, b, 0, 0);
}
 
static int Add(int a, int b, int c)
{
  return Add(a, b, c, 0);
}
 
static int Add(int a, int b, int c, int d)
{
  return (a + b + c + d);
}

In this example we can call Add with 2, 3 or 4 parameters and only one functional method is called - the others just pass the data around. You can use IntelliSense to see the different overloads and their parameters.

Overloading Example
Overloading Example

Full Code

using System;
using System.Drawing;
 
class TestBaseCarClass
{
    static void Main()
    {
        Console.WriteLine(Add(1, 1));
    }
 
    static int Add(int a, int b)
    {
        return Add(a, b, 0, 0);
    }
 
    static int Add(int a, int b, int c)
    {
        return Add(a, b, c, 0);
    }
 
    static int Add(int a, int b, int c, int d)
    {
        return a + b + c + d;
    }
}
 

This code is only shown as an example. If you really want to create a function that accepts any number of parameters of the same type, then the params keyword is better suited.

Method Overriding

Methods can be overridden, replacing the default behaviour or extending it. In this example we will override the ToString() method for the ArrayList class so that it may return something more meaningful.

using System;
using System.Collections.Generic;
 
class Program
{
  static void Main()
  {
     ArrayList myList = new ArrayList();
 
    myList.Add("Hello");
    myList.Add("World");
    myList.Add("123456");
 
    Console.WriteLine(myList.ToString());
  }
}

You may expect the ToString to return "Hello World 123456", but it actually returns "System.Collections.ArrayList", which isn't particularly helpful.

We can override this default behaviour by creating a new class, which inherits from the ArrayList. We can then override the ToString method by using the override keyword.

class myArrayList : System.Collections.ArrayList
{
    public override string ToString()
    {
        return base.ToString();
    }
}

The base keyword is used to refer to the object. By default, the override method will return base.ToString(). You can use this base object to call the non-overwritten method. We are going to amend the code to suit our needs, and in this example, we are going to convert the array list to show the contents of the list, one item per line.

using System;
using System.Text;
using System.Collections.Generic;
 
class Program
{
    static void Main()
    {
        myArrayList myList = new myArrayList();
 
        myList.Add("Hello");
        myList.Add("World");
        myList.Add("123456");
 
        Console.WriteLine(myList.ToString());
    }
}
 
class myArrayList : System.Collections.ArrayList
{
    public override string ToString()
    {
        StringBuilder result = new StringBuilder();
        string[] theItems = (string[])base.ToArray(typeof(string));
 
        foreach (string item in theItems)
        {
            result.AppendLine(item);
        }
        return result.ToString();
    }
}

Now when we create an instance of our new class and call the ToString method we are given "Hello World 123456" one item per line.

Summary and Concusions

In this rather lengthy article, we have looked at what Object-Oriented Programming is, learned what a class is, what properties are, how to inherit classes, constructors and destructors, overloading and overriding. In Part 2 we will look at some more object orientated examples and a few more concepts.

Last updated on: Thursday 11th October 2018

 

Comments

Have a question or suggestion? Please leave a comment to start the discussion.

 

Leave a Reply

Please keep in mind that all comments are moderated according to our privacy policy, and all links are nofollow. Do NOT use keywords in the name field. Let's have a personal and meaningful conversation.

Your email address will not be published.