Singleton Design Pattern Evolution and implementation C#

Introduction

Hi, Today We will learn about the Singleton Design Pattern in C#. We will learn about different ways to implement the Singleton Design Pattern.

Checkout this video tutorial for detail implementation - http://www.youtube.com/watch?v=jSuob32wlHQ

What is Singleton Pattern in C#?

Singleton Pattern says, “Define a class that has only one instance and provides a global point of access”. In other words, a class must ensure that only a single instance should be created, and all other classes can use a single object.

To create the Singleton Pattern, we need to implement the following points,

  1. Private Constructor to avoid object creation.
  2. Locking Mechanism for thread-safe access in multi-thread environment.
  3. Static object instance to access singleton object.

Let's Start the evolution of the Singleton Design Pattern

Case 1. We have applied the lock before accessing the object, which prevents other threads from accessing an object in a concurrent manner, and it is a very poor design; the Lock is only needed till the first instance is not created.

public sealed class Singleton {

  private static object objectInstance = new object();
  private static Singleton instance = null;
  private static int counter = 0;
  // To avoid to make object
  private Singleton() {
    counter++;
    Console.WriteLine("Called instatnce {0} times", counter);
  }
  public static Singleton GetInstance {
    get {
      // T1 ,T2, T3,T4  Thread accessing instance
      #region
      lock(objectInstance) {
        if (instance == null) instance = new Singleton();
        return instance;
      }
    }
  }
}

Case 2. Now Lock mechanism is only needed till the first instance is not created. Once an object is created, no lock is required. This design has good performance in accessing, but we still have issues with thread safety the first time.

// sealed keyword is used to avoid the inheritance so child class will not be created
public sealed class Singleton {

  private static object objectInstance = new object();
  private static Singleton instance = null;
  private static int counter = 0;
  // To avoid to make object
  private Singleton() {
    counter++;
    Console.WriteLine("Called instatnce {0} times", counter);
  }
  public static Singleton GetInstance {
    get {
      // T1 ,T2, T3,T4  Thread accessing instance
      if (instance == null) {
        lock(objectInstance) {
          instance = new Singleton();
        }
        return instance;
      }
      else return instance;
    }
  }
}

Case 3. To make this design completely thread-safe. I introduce the double locking mechanism. Let's see how this work.

public sealed class Singleton {

  private static object objectInstance = new object();
  private static Singleton instance = null;
  private static int counter = 0;
  // To avoid to make object
  private Singleton() {
    counter++;
    Console.WriteLine("Called instatnce {0} times", counter);
  }
  public static Singleton GetInstance {
    get {
      // T1 ,T2, T3,T4  Thread accessing instance
      #region Double checked Locking For Multi - thread enviornment
      if (instance == null) {
        lock(objectInstance) {
          if (instance == null) instance = new Singleton();
        }
        return instance;
      }
      else return instance;#endregion
    }
  }
}

Finally, we have achieved the required code to create a singleton Object. But As we know, programming is all about evolution, so in C#

Case 4. We have a concept of lazy Initialization, which is quite thread-safe and memory-optimized. Let's see how to implement Singleton Design Pattern using Lazy Loading.

Checkout this link for more detail regarding Lazy Initialization https://learn.microsoft.com/en-us/dotnet/framework/performance/lazy-initialization

// sealed keyword is used to avoid the inheritance so child class will not be created
public sealed class Singleton {

  private static int counter = 0;
  // To avoid to make object
  private Singleton() {
    counter++;
    Console.WriteLine("Called instatnce {0} times", counter);
  }#region Thread Safe Instance

  private static Lazy < Singleton > instance = new Lazy < Singleton > (() =>new Singleton());
  public static Singleton GetInstance {
    get {
      {
        return instance.Value;
      }
    }
  }
  #endregion
}

Conclusion

A design pattern is the reusable form of a solution to a design problem. Singleton's Design pattern falls under the Creational Design Pattern category.

There are some use cases where a singleton design pattern is needed,

  • When you want to control the number of class instances to be created.
  • When you want to have a single instance of a class that you can access from multiple threads.
  • When only a single instance of a class is needed to control the action throughout the execution.
  • When you want to maintain only one copy of the shared data.