Singleton Design Pattern in C#

What is Singleton?

In this post I’m going to talk about the Singleton design pattern. It is used widely across almost every project, with the idea to guarantee a single instance of an object, for the whole application. First of all, Singleton is not a global variable โ€“ it is an instance of an object, which is always the same, no matter how many times you access it.

When to use Singleton?

Use the Singleton Design Pattern in one of the following scenarios:

  • When the creation of the object is too expensive โ€“ for instance, a Database connection โ€“ it is more efficient to create it once and reuse it, than create it, again and again, each time you need it.
  • Shared resource in multithreaded applications โ€“ for instance, a logger โ€“ it doesnโ€™t make sense to have more than one logger. If your application is multithreaded, your logger must be shared between all the threads. After all, you want all your logs to go to one specific place, instead of creating a new logger each time, a thread needs to log something.
  • When our application needs global access to an object instance โ€“ a good example of this in .NET is the Console object. It is only one instance and it is accessed from everywhere. Many times our applications depend on different modules, which may need a shared global instance. Another good example is a cache object. If you have any cached data, you will want all your threads to work with the same instance of the cache.

How to create Singleton?

Here is an example of how to create a Singleton in C#:

C#
public class Singleton
{
    private static Singleton instance;

    private Singleton()
    { }

    public Singleton Instance
    {
        get
        {
            if (instance == null)
                instance = new Singleton();

            return instance;
        }
    }
}

The class is declared with a private constructor โ€“ this is because it should only be instantiated from the inside. It is just exposing an Instance property to the outside world, and this property takes care of instantiating the object. Therefore, it is his responsibility to create the instance, if it is not already created, and no one else should be able to do that. In this way the instance creation logic is encapsulated in the property and it is executed only when needed – this is called lazy initialization.

Singleton in multithreaded applications

This was a very basic example of what Singleton is, and how can be implemented. However, this is not the correct implementation and it wonโ€™t work as expected in multithreaded applications. But, why is that?

Letโ€™s imagine you have more complex logic, and a lot of steps are required for instance creation. If two threads hit the instance==null check, one of them will be faster and will go inside the instance creation logic. But until it is finished, the other thread may also enter because the instance will still be null, and eventually the two threads will create two instances, which is the opposite of the singleton concept.

Singleton design pattern - no thread safe
Singleton design pattern – no thread safe

To fix this we can add a lock, which will block any thread if one is already inside the lock, like this:

C#
public class Singleton
{
    private static Singleton instance;

    private static object instanceLock = new object();

    private Singleton()
    { }

    public Singleton Instance
    {
        get
        {
            lock (instanceLock)
            {
                if (instance == null)
                    instance = new Singleton();
            }

            return instance;
        }
    }
}

But, this can cause performance deadlock if there are too many threads waiting for a single one to finish. Every time Instance is called, all threads will wait on the lock, except the one inside, no matter if the instance is created or not. So, to fix this we will add another null check, just before the lock:

C#
public class Singleton
{
    private static Singleton instance;

    private static object instanceLock = new object();

    private Singleton()
    { }

    public Singleton Instance
    {
        get
        {
            if (instance == null)
            {
                lock (instanceLock)
                {
                    if (instance == null)
                        instance = new Singleton();
                }
            }

            return instance;
        }
    }
}

After that change, the deadlock will be only the first time. When the single instance is created, no threads will go to the lock block of code.

Drawbacks

I already mentioned when to use the Singleton, but here are some drawbacks of the design:

  • Because of the private constructor, we are not able to inherit it. So, the Open Closed principle is broken, because the class is not open for modifications.
  • Single responsibility is also broken โ€“ the singleton has at least two responsibilities โ€“ creating itself and doing its regular work (logging, database connection, etc.).
  • Can be hard to test because of the static instance property.

Conclusion

Yes, they are some notable drawbacks of this pattern, related to two main SOLID principles, but in some cases violating them may not be so bad, if we are solving bigger problems. The Singleton may improve application performance, by dealing with hard-to-create objects instances, which, otherwise, can bring you bigger problems, from not-following the SRP and Open-Closed principles.