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

C# Object-Oriented Programming Part 2

By on in Coding

2,024 words, estimated reading time 10 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#

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 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.

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.

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 implementing an interface. This can be done by first specifying the base class, then a comma-separated list of interfaces.

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 ModifierDescription
publicA public member is accessible from anywhere. This is the least restrictive access modifier.
protectedA protected member is accessible from within the class and all derived classes. No access from the outside is permitted.
privateA private member is accessible only from within the same class. Not even derived classes can access it.
internalAn 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 internalinternal 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.

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()?

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.

  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 ran?

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.

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 vehicle will have different implementation 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 own specialised implementation of the method.

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

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

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

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 for 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 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 for the purpose of this example.

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 for 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.

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 own implementation.

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.

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.

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 own 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 own 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 on test2 and test1 will fail. The base class does not contain an entry for AnotherTest thus the compiler will error. There is, however, a way better method which is called Boxing and Unboxing which we will look at in a later tutorial.

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
    }
  }
}
 

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.