Introduction to C# Object-Oriented Programming Part 2

A further look at C# Object-Oriented Programming where see interfaces, namespaces, partial and abstract classes.

2,017 words, estimated reading time 8 minutes.
Introduction to Programming with C#

This article is part of a series of articles. Please use the links below to navigate between the articles.

  1. Learn to Program in C# - Full Introduction to Programming Course
  2. Introdution to Programming - C# Programming Fundamentals
  3. Introduction to Object Oriented Programming for Beginners
  4. Introduction to C# Object-Oriented Programming Part 2
  5. Application Flow Control and Control Structures in C#
  6. Guide to C# Data Types, Variables and Object Casting
  7. C# Collection Types (Array,List,Dictionary,HashTable and More)
  8. C# Operators: Arithmetic, Comparison, Logical and more
  9. Using Entity Framework & ADO.Net Data in C# 7
  10. What is LINQ? The .NET Language Integrated Query
  11. Error and Exception Handling in C#
  12. Advanced C# Programming Topics
  13. All About Reflection in C# To Read Metadata and Find Assemblies
  14. What Are ASP.Net WebForms
  15. Introduction to ASP.Net MVC Web Applications and C#
  16. Windows Application Development Using .Net and Windows Forms
  17. Assemblies and the Global Assembly Cache in C#
  18. Working with Resources Files, Culture & Regions in .Net
  19. The Ultimate Guide to Regular Expressions: Everything You Need to Know
  20. Introduction to XML and XmlDocument with C#
  21. Complete Guide to File Handling in C# - Reading and Writing Files

In this tutorial, we will continue from where we left off when we looked at Object-Oriented Programming. We will have a look at interfaces, namespaces, static, partial and abstract classes, and some advanced class initialisation.

C# Interfaces and Classes

An interface can be thought of as a contract between a class and a service. Any class that implements an interface must fully implement all aspects of the service specified by the interface. This ensures that when you use a class that inherits from an interface you know that it implements all the required functionality.

Interfaces cannot contain constants, data fields, constructors, destructors and static members. It can only contain the signatures of methods, properties, delegates or events. All the member declarations inside the interface are implicitly public. If your abstract class only provides abstract methods then you can create an interface.

It is good practice to name interfaces with a capital I and also capitalise the first letter, for example, an interface called PetrolEngine would be named IPetrolEngine. This way you (and any other developers) know that it is an interface.

Although C# does not support multiple inheritances directly, your classes and structs can inherit from a base class as well as multiple interfaces. Let's look at a few classes and interface examples.

C#
interface ITestInterface
{
  public void MethodToImpliment();
}

class TestClass : ITestInterface
{
}

Classes implement interfaces in the same way that classes inherit from base classes. Because our TestClass implements ITestInterface, we must provide an implementation of MethodToImpliment or the compiler will throw an error.

C#
class TestClass : ITestInterface
{
  public MethodToImpliment()
  {
    Console.WriteLine("Test Method Called!");
  }
}

You may find that you need to inherit from a base class as well as implement an interface. This can be done by first specifying the base class, then a comma-separated list of interfaces.

C#
class TestClass : BaseClass, ITestInterface
{
  public MethodToImpliment()
  {
    Console.WriteLine("Test Method Called!");
  }
}

class TestClass : BaseClass, ITestInterface1, ITestInterface2
{
  public MethodToImpliment()
  {
    Console.WriteLine("Test Method Called!");
  }
}

Access Modifiers and Scope

In almost all programming languages you will see variables, methods and objects prefixed with either public, private, protected or internal.

These keywords are called Access Modifiers and they define the scope of the object, that is who or what can see it.

Access Modifier Description
public A public member is accessible from anywhere. This is the least restrictive access modifier.
protected A protected member is accessible from within the class and all derived classes. No access from the outside is permitted.
private A private member is accessible only from within the same class. Not even derived classes can access it.
internal An internal member is accessible from within any part of the same Microsoft .NET-based assembly. You can think of it as public at the assembly level and private from outside the assembly.
protected internal internal protected member is accessible from within the current assembly or from within types derived from the containing class.

When declaring objects, by default there is no scope defined and all objects are created with the private scope.

Let's look at a simple demo class, and investigate the difference between public and private objects.

C#
public class Demo
{
  private int x;
  public int y;
 
  public void Demo()
  { 
    x = 1;
    y = 2;
  }

  public myFunction(int x)
  { 
    Console.WriteLine(x);
  }
}

class Program
{
  static void Main()
 {
    Demo demo1 = new Demo();
    Console.WriteLine(demo1.x); / Error Here
    Console.WriteLine(demo1.y); 
 }
}

When you try and compile this code, the compiler will fail and show an error: "Demo.x is not accessible due to its protection level". This is because x has been declared as a private variable of the Demo class. It is however accessible from within the Demo class.

What do you think would be the result if we called myFunction from within Main()?

C#
myFunction(10);

Would it output 1 or 10?

Local variables or method parameters take priority over other all other scope levels, so the method will output the value of x as passed in as a parameter, i.e. 10.

If you need to access the value of x declared as a field of the class you can use the this keyword. The keyword this is a reference to the current object's instance.

C#
  public myFunction(int x)
  { 
    Console.WriteLine(x); / Outputs 10
    Console.WriteLine(this.x); / Outputs 1
  }

How about this example? What do you think the outcome would be if this code were compiled and run?

C#
public class Demo
{
  private int x;
  public int y;

  public void Test()
  {
    int myInt = 1;
  }
}

class Program
{
  static void Main()
  {
    Console.WriteLine(Demo.myInt);
  }
}

Will this work? No. The variable myInt only exists for the lifecycle of the Test method. myInt does not exist until Test is called, and myInt is destroyed when the Test method exits.

There are two other lesser-used access modifiers called protected and internal. Protected objects are only accessible from within the class or another class that inherits from it. Internal objects are only available from within the compilation unit (assembly, dll or application).

If you are not familiar with inheritance, you may wish to read through the class inheritance tutorial before continuing.

Going back to the original Demo class, let's have a look at the protected access modifier. I have added a protected integer called z to help illustrate the access modifier.

C#
public class Demo
{
  private int x;
  public int y;
  protected int z;
 
  public void Demo()
  { 
    x = 1;
    y = 2;
    z = 3;
  }
}

public class AnotherSampleDemo : Demo
{
  public void AnotherSampleDemo()
  { 
    x = 100;
    y = 1000;
    z = 10000;
  }
}

class Program
{
  static void Main()
 {
    Demo demo1 = new Demo();
    Console.WriteLine(demo1.x);
    Console.WriteLine(demo1.y); 
    Console.WriteLine(demo1.z); 
 }
}

When compiling this code, there will be two errors. Firstly, the Program class cannot access the private integer x, nor can it access the protected integer z. The second error is that the AnotherSampleDemo, although inherited from Demo class, does not have access to the private integer x, but it does have access to the protected integer z. This allows the Demo class to retain control over internal variables.

In real-world applications, you should always encapsulate all variables with a property instead of giving access directly to the variable itself.

Abstraction

Abstract classes provide a way to force an inherited class to implement an override method, similar to, but not the same as an interface.

Unlike an interface, abstract classes are fully functional classes, but you can force one or more methods to be overridden.

Let's say you had a theoretical vehicle class. Every vehicle must be able to steer (i.e. must have a steer() method) however different types of vehicles will have different implementations of this method. It is pointless to implement this method in the base class as the code will not be used and could not possibly contain enough information for the derived class to use. It is also dangerous to leave it empty as it could be called without any implementation causing the resulting vehicle to be unable to steer! By marking the method as abstract, you force any derived classes to have their specialised implementation of the method.

Both the class and the method must be marked as abstract. Because there is no implementation of the steering method, the statement is terminated with a semi-colon.

C#
public abstract class Vehicle
{
  public abstract void steer();
}

Now, each class that inherits from the Vehicle class must implement the steer method.

C#
public class Car : Vehicle
{
  public override void steer()
  {
    / Implement a steering wheel
  }
}

public class Bike : Vehicle
{
  public override void steer()
  {
    / Implement handlebars
  }
}

public class Tank : Vehicle
{
  public override void steer()
  {
    / Implement tracked drive steering
  }
}

public class Airplane : Vehicle
{
  public override void steer()
  {
    / Implement ailerons and rudders
  }
}

This ensures that all inherited classes always have a specific implementation of the abstract method.

Encapsulation

Encapsulation is the ability of an object to hide its data and methods from those who do not need to know and only expose data and methods that are required.

Techniques for encapsulation include class abstraction and properties. Sometimes you may not wish for direct interaction with an object's data field, for example, in a bank account class, you would not want to expose a balance field where it could possibly be modified without any checks or security.

Encapsulation is the process of protecting the data using properties so that any attempt to modify the data is done through the proper channels, ideally with an audit log for the bank account class.

Using the rather cliche BankAccount class as an example, we can encapsulate the account balance to prevent invalid or unwanted access. Yes, I know that this is not how a bank account should function, but it serves the purpose of this example.

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication2
{
    class BankAccount1
    {
        public decimal Balance;
    }

    class BankAccount2
    {
        private decimal _balance;
        public decimal Balance
        {
            get 
            { 
                return _balance; 
            }
            
            set 
            { 
                / do security checks and validation
                if (value > 0)
                    _balance = value;
                else
                    throw new InvalidOperationException();
            }
        }
    }


    class Program
    {
        static void Main()
        {
            BankAccount1 ba1 = new BankAccount1();
            ba1.Balance = -100; / Dangerous as this is allowed

            BankAccount2 ba2 = new BankAccount2();
            ba2.Balance = -100; / This is now not allowed and raises an error
        }
    }
}

Polymorphism in C#

Another useful technique in object-orientated programming is Polymorphism, which allows you to implement derived class methods through a base class pointer at runtime.

This is useful when you need to invoke the methods of objects stored in an array if the objects are not always of the same type. They must be related through inheritance, however, and they have to be added as an inherited type.

C#
class testClass
{
  public virtual void testMethod()
  {
    Console.WriteLine("I am a testClass");
  }
}

We can create a series of derived test classes which inherit from testClass and which also override the testMethod to give their implementation.

C#
class test1 : testClass
{
  public override void testMethod()
  {
    Console.WriteLine("I am test 1");
  }
}

class test2: testClass
{
  public override void testMethod()
  {
    Console.WriteLine("I am test 2");
  }
}

class test3 : testClass
{
  public override void testMethod()
  {
    Console.WriteLine("I am test 3");
  }
}

Now we can create an array of test objects and iterate through them using a foreach loop. Full code listing is shown for a console application.

C#
using System;
using System.Collections.Generic;
using System.Text;

namespace ConsoleApplication2
{
    class testClass
    {
        public virtual void testMethod()
        {
            Console.WriteLine("I am a testClass");
        }
    }
    
    class test1 : testClass
    {
        public override void testMethod()
        {
            Console.WriteLine("I am test 1");
        }
    }

    class test2 : testClass
    {
        public override void testMethod()
        {
            Console.WriteLine("I am test 2");
        }
    }

    class test3 : testClass
    {
    }


    class Program
    {
        static void Main()
        {
            testClass[] testArray = new testClass[4];

            testArray[0] = new test1();
            testArray[1] = new test2();
            testArray[2] = new test3();
            testArray[3] = new test2();

            foreach (testClass test in testArray)
            {
                test.testMethod();
            }
        }
    }
}

When running the program the overridden testMethod of each object is called, and for the test3 since it does not override testMethod, the testMethod from the base class is called.

C#
I am test 1
I am test 2
I am a testClass
I am test 2
Press any key to continue . . .

If your base class for the array were test2 instead of testClass, you would not be able to add test1 or test3 to the array. This technique only works if your objects are, or inherit from, the base class of the array. Why? Because test2 could have its method defined which is not defined within test1 or test2, but a method defined in testClass is guaranteed to be inherited in test2.

It must also be noted that you can only access the methods and properties of the base class, thus if a derived class has its method or property, it cannot be accessed (as shown in the example below). The AnotherTest method only exists for test class "test3", thus calling it test2 and test1 will fail. The base class does not contain an entry for AnotherTest thus the compiler will error.

C#
class test3 : testClass
{
  public void AnotherTest()
  {
    Console.WriteLine("This is AnotherTest");
  }
}

class Program
{
  static void Main()
  {
    testClass[] testArray = new testClass[4];

    testArray[0] = new test1();
    testArray[1] = new test2();
    testArray[2] = new test3();
    testArray[3] = new test2();

    foreach (testClass test in testArray)
    {
      test.AnotherTest(); / Compiler Error
    }
  }
}
Was this article helpful to you?
 

Related ArticlesThese articles may also be of interest to you

CommentsShare your thoughts in the comments below

If you enjoyed reading this article, or it helped you in some way, all I ask in return is you leave a comment below or share this page with your friends. Thank you.

This post has 4 comment(s). Why not join the discussion!

We respect your privacy, and will not make your email public. Learn how your comment data is processed.

  1. BE

    On Tuesday 3rd of March 2015, Benjamin said

    Very nice explanation,easy to understand.
    Thanks Tim Trott

  2. NA

    On Monday 13th of February 2012, Naeem said

    Very nice example... totally clear

  3. AR

    On Tuesday 27th of December 2011, Arjunan said

    Really Nice example for Boxing and Unboxing..Clear Explanation...Keep it up..

  4. TA

    On Wednesday 16th of February 2011, tamir said

    nice one this is real advance!
    keep on the great work!