Singleton Design Pattern With C# Sample

What is Singleton Design Pattern?

A singleton design pattern is a creational design pattern. It is used to maintain a single instance throughout the application.

When to use a Single Design Pattern?

When our requirement requires a single instance through the application and if it has to be inherited by some other interface/class or if memory management should be dynamic we can take steps to implement this design pattern.

If our aim is to create only a single instance I recommend using a static class.

Where we can use it?

For Database connection, logging, caching purposes, etc.

Rules to implement Singleton

  1. It should have Global access
  2. It should not be inherited by some other class; i.e., we have to seal the class with a sealed keyword / we can make the constructor private to stop inheritance.
  3. Constructor should be Private, to prevent the creation of instances from outside the class. And once we define the constructor as private it will also help to stop being inherited.

Below is the code that defined the Singleton design pattern. The below code is not thread-safe.

namespace Singleton.Design.Pattern
{
    public class SingletonSample
    {
        private static SingletonSample _instance;

        /// <summary>
        /// Instance will set only once initially when _instance is null
        /// </summary>
        public static SingletonSample Instance => _instance = _instance ?? new SingletonSample();

        /// <summary>
        /// To prevent inheritance of this class and to prevent the creation of instance from outside the class
        /// </summary>
        private SingletonSample()
        {
            
        }
    }
}

Below is the code for accessing singleton instances. To make sure we have a single instance, I am accessing the class with Instance 10 times and matching with some resistance object, along with printing object reference hash code

using System.Collections.Generic;
using static System.Console;

namespace Singleton.Design.Pattern
{
    class Program
    {
        public static void Main()
        {
            List<SingletonSample> instances = new List<SingletonSample>();
            for (int i = 0; i < 10; i++)
            {
                instances.Add(SingletonSample.Instance);
            }

            var refInstance = SingletonSample.Instance;
            WriteLine($"Object refInstance Hash code:{refInstance.GetHashCode()}");
            foreach (var instance in instances)
            {
                if (instance.Equals(refInstance))
                    WriteLine($"Found same object with Hash code:{instance.GetHashCode()}");
                else
                    WriteLine($"Found mismatch object with Hash code:{instance.GetHashCode()}");
            }
            
            ReadLine();

        }
    }
}

Below is the output window after the execution of the above code.

Output window

If we are using the threads in our application and we are expecting to maintain a singleton instance even though the class is accessed by threads, in this kind of case we need thread safe singleton class.

Below is the thread-safe code.

using System;

namespace Singleton.Design.Pattern
{
    public class ThreadSafeSingleton
    {
        private static ThreadSafeSingleton _instance;
        private static object threadSafe = new object();
        public static string Praposal;

        public static ThreadSafeSingleton GetInstance(string proposal)
        {
            //lock keyword which will be used to synchronize threads
            lock (threadSafe)
            {
                if (_instance is null)
                {
                    Praposal = proposal;
                    _instance = new ThreadSafeSingleton();
                }
            }
            return _instance;
        }

        private ThreadSafeSingleton()
        {
            
        }

        public void PrintProposalReply()
        {
            Console.WriteLine(Praposal);
        }
    }
}

In thread-safe code, we can see that we are using the lock keyword which will be used to synchronize threads. If multiple threads try to access the instance of a class, the first will acquire the lock and will proceed to the next, while the rest will wait until the first thread finishes its execution in that class. Again when the second thread, third thread, and so on move one by one synchronously, and as the first thread is an already created instance, the rest of the threads will use the same instance for further execution.

Below is the code where we are creating multiple instances in 4 different threads and while creating each instance we are passing the thread name. As per the above explanation, the class should maintain the proposal of the first thread which is hit to thread safe singleton class.

using System;
using System.Collections.Generic;
using System.Threading;
using static System.Console;

namespace Singleton.Design.Pattern
{
    class Program
    {
        public static void Main()
        {
            Thread threadA = new Thread(() => PrintThreadName(nameof(threadA)));
            Thread threadB = new Thread(() => PrintThreadName(nameof(threadB)));
            Thread threadC = new Thread(() => PrintThreadName(nameof(threadC)));
            Thread threadD = new Thread(() => PrintThreadName(nameof(threadD)));

            threadA.Start();
            threadB.Start();
            threadC.Start();
            threadD.Start();
            threadA.Join();
            threadB.Join();
            threadC.Join();
            threadD.Join();

            ReadLine();
        }

        private static void PrintThreadName(string proposal)
        {
            var s = ThreadSafeSingleton.GetInstance(proposal + " Proposed to create instance");
            s.PrintProposalReply();
        }
    }
}

Below is the output of the Thread Safe Singleton accessing code. In the below output, we can see that thread A hit the Thread Safe Singleton code and created an instance and the other 3 threads i.e. thread, threads, threads, access the same instance created by thread

ThreadA

Summary

In this article, we learned how to create a singleton instance and where to use it. And also we saw how to implement thread-safe singleton instances.


Similar Articles