Secret Survival Guide to Singleton Design Pattern in C#


Being a developer, you must have heard about and worked with different design patterns in C#. The Gang of Four (GoF) designed 23 design patterns to help developers solve different programming problems. One of those design patterns is the Singleton design pattern which you will learn about in this article.

The Singleton design pattern is a creational design pattern that ensures that a class has only one instance and provides a global point of access to that instance. The Singleton class is responsible for creating and managing its own instance.

This tutorial will introduce the Singleton pattern, its use cases, 6 different ways to implement the Singleton design pattern in C#, and its benefits.

What is the Singleton design pattern?

singleton design pattern

One of the most popular creational design patterns is the Singleton pattern. This design pattern implies that you should create only one Instance of a class and provide a single point of access to access that instance. In other words, you can say that Singleton is a class that allows you to create only one instance of it and gives you global access to it.

The Singleton classes have the same advantages and disadvantages as Globals. They affect the modularity of your code.

When to use the Singleton design pattern?

You use the Singleton design pattern when you want to have only one Instance of a class. The class that implements this pattern is called the Singleton class. A Singleton class does not allow you to create any instance if its Instance already exists. The Singleton class also gives global but controlled access to its Instance.

6 Ways to implement Singleton design pattern in C#

There are various ways of implementing the singleton design pattern in C#.

In this section, you will see six ways to implement it but before proceeding, have a look at what is common in all 6 methods:

  • There will be a single private and parameterless constructor to prevent other classes from creating instances of the singleton class.
  • The class is sealed to restrict inheritance. If any of the derived classes can create an object, it will violate the pattern.
  • A static variable that holds the Instance of singleton class throughout the program.
  • A public static method for getting the reference to the Instance.

Now that you know some common characteristics of all methods let’s move to the implementation.

No thread-safety

Code:

public sealed class SingletonPattern
{
    private static SingletonPattern instance = null;

    //private constructor
    private SingletonPattern()
    { }

    //property to access the Instance
    public static SingletonPattern Instance
    {
        get
        {
            //checking if the Instance already exists
            if (instance == null)
            {
                instance = new SingletonPattern();
            }
            return instance;
        }
    }
}

Explanation:

As stated earlier, this does not ensure thread safety as two different threads can find the Instance of the Singleton to be none and create the instances while violating the Singleton pattern. Although the Instance may exist before the if(instance == null) evaluates, other threads might not know about it unless they pass certain memory barriers.

Hence, experts do not recommend using this way to implement the Singleton design pattern.

Simple thread-safety

Code:

public sealed class SingletonPattern
{
    private static SingletonPattern instance = null;
    private static object lock_code = new object();

    //private constructor
    private SingletonPattern()
    { }

    //property to access the Instance
    public static SingletonPattern Instance
    {
        get
        {
            //locking the shared object
            lock (lock_code)
            {
                //checking if the Instance already exists
                if (instance == null)
                {
                    instance = new SingletonPattern();
                }
                return instance;
            }
        }
    }
}

Explanation:

The above code is somewhat thread safe as the thread locks the shared object before checking for the instance. The locking ensures that all the reads occur after a particular object locks, and unlocking ensures that all the writes occur before the lock releases.

Locking also removes the memory barriers and ensures that only one thread creates an instance. This way, only one thread will be in that part of the code, and it will create the object before the second thread enters the code. This way, the expression will evaluate as false.

Although this implementation ensures thread safety, it affects the performance as the lock is required every time a class refers to the instance.

Double-check locking to ensure thread safety

Code:

public sealed class SingletonPattern
{
    private static SingletonPattern instance = null;
    private static object lock_code = new object();

    //private constructor
    private SingletonPattern()
    { }

    //property to access the Instance
    public static SingletonPattern Instance
    {
        get
        {
            //Check 1
            if (instance == null)
            {
                //locking the shared object
                lock (lock_code)
                {
                    //Check 2
                    if (instance == null)
                    {
                        instance = new SingletonPattern();
                    }
                }
            }
            return instance;
        }
    }
}

Explanation:

This implementation checks for the Instance two times before creating one. Although it ensures the thread-safety without taking out the locks, there are some drawbacks of this method:

  • This implementation breaks ECMA CLI specifications as there are no memory barriers. The .NET memory model may make it safer as it is better than ECMA specifications. Making the instance variables volatile or calling the memory barriers can make it work, but still, you should try to avoid something that the experts do not suggest.
  • Making mistakes is relatively easier. Any minor changes in the given code can cause performance issues or affect accuracy.
  • Java does not support this implementation as its memory model does not ensure that the constructor is complete before the new object’s reference is assigned to the Instance.

This point may seem odd here as this article covers C# only, but C# developers can also be Java developers and may need to know this.

Having that said, let’s move to a better implementation.

No lock thread-safe function

Code:

public sealed class SingletonPattern
{
    private static SingletonPattern instance = new SingletonPattern();

    static SingletonPattern()
    { }
    //private constructor
    private SingletonPattern()
    { }

    //property to access the Instance
    public static SingletonPattern Instance
    {
        get
        {
            return instance;
        }
    }
}

Explanation:

This implementation is quite simple and easy, but you may ask how it is thread-safe and how lazy it is. So here is the answer:

As you might know, the static constructors in C# execute once per AppDomain, and only when an instance is created or referenced. The compiler must execute a new type check no matter what else is happening. This check is faster than the double-check method as it checks for the currently created type. However, this implementation also has some drawbacks:

  • This is less lazy than other implementations as the instance creation will need a reference to the static members other than the instance.
  • If a static constructor invokes another static constructor, it creates problems. Static constructors have some consequences as they refer to each other in a cycle.
  • When the type does not have a beforefieldinit flag, only .NET assures the laziness of type initializers. Unfortunately, the C# compilers mark the types with no static constructors as beforefieldinit.

Full lazy instantiation

Code:

public sealed class SingletonPattern
{

    //private constructor
    private SingletonPattern()
    { }

    //First reference to the static member of nested class
    public static SingletonPattern Instance
    {
        get
        {
            return NestedSingleton.instance;
        }
    }
    private class NestedSingleton
    {
        //Static constructor let C# compiler know not to
        //mark type as beforefieldinit
        static NestedSingleton()
        { }

        internal static readonly SingletonPattern instance =
            new SingletonPattern();
    }
}

Explanation:

In this implementation, the instantiation occurs when the static member of the static class is first referenced, i.e., inside the Instance. Although this implementation is fully lazy, it overcomes the performance issues of the previous implementation. 

Also, note that the nested class can access its parent’s private members. But the parent cannot access the nested class instance field if you don’t mark it as internal.

.NET 4 Lazy<T> type

The .NET 4 and higher versions provide System.Lazy<T> to make laziness super simple. All you have to do is pass a delegate to the constructor that calls the Singleton constructor with the help of lambda expression as follows:

public sealed class SingletonPattern
{

    private static readonly Lazy<SingletonPattern> lazyobj =
        new Lazy<SingletonPattern>(() => new SingletonPattern());
    
    public static SingletonPattern Instance
    {
        get
        {
            return lazyobj.Value;
        }
    }

    private SingletonPattern() { }
}

Explanation:

This simple implementation performs well and also allows you to check if the instance exists or not by using the IsValueCreated property.

Lazy<T> uses LazyThreadSafetyMode.ExecutionAndPublication as default thread safety mode. You might want to try other modes to meet your requirements.

Benefits of the Singleton design pattern

advantages

Although implementing the Singleton pattern may seem useless, there are some benefits of using it:

  • Controlled access to the sole instance – The Singleton class has strict control over the access of its sole instance as it encapsulates it. It controls how and when clients access its instance.
  • Permits a variable number of instances – This pattern allows you to create multiple instances of the Singleton class if needed. Moreover, you can use this same approach to control an application’s instances by simply changing the method responsible for granting access to the instance.
  • Lazy initialization – The lazy initialization ensures that the object is created only if needed. This way, the Singleton pattern helps prevent the wastage of resources, specifically memory.

What are the disadvantages of the Singleton design pattern?

The Singleton design pattern is a widely used software design pattern that can be useful in cases where you need to limit the number of resources that are used or where you need to ensure that only one instance of a class exists. However, the Singleton design pattern also has a few disadvantages.

The disadvantages of the Singleton design pattern are:

  • The global nature of the singleton design pattern leads to dependency hiding – When you use the Singleton design pattern in the application, it can be more difficult to identify it in the class that uses it. While you generally pass the dependencies through the constructor, the singleton object can be straightly used in the code without passing through the constructor. For someone new to the codebase, this could be confusing.
  • It can be difficult to unit test code that uses the Singleton pattern – Unit testing code that uses the Singleton pattern can be difficult because the code is designed only to allow one instance of an object to be created. This can make creating multiple instances of the object difficult for testing purposes. Additionally, the code may rely on a global state, making it difficult to isolate tests and ensure they are repeatable. In case you need to test the code that uses the Singleton, I have a separate article on it.
  • It can lead to tightly coupled code and is difficult to change – The Singleton class typically exposes its methods and properties through a static interface. Any code that uses the Singleton is tightly coupled to its implementation. This can make it difficult to change the code that uses the Singleton class.
  • There is only one instance of the object, so if it becomes corrupted, the entire system is compromised – The problem with the Singleton pattern is that there is only one instance of the object. So if it becomes corrupted, the entire system is compromised. The Singleton pattern is often used in system-level objects, such as database connections and file handles. If these objects become corrupted, it can bring down the entire system.

To find out how to eliminate those Singleton disadvantages, check out the separate article about it.

Is Singleton design pattern bad?

Because of many disadvantages, developers generally label the Singleton design pattern as bad or anti-pattern. The Singleton in itself is not bad. It becomes bad when it’s misused and used for storing the global state.

To investigate the question further, I asked 888 developers if they think the Singleton design pattern is bad. Here is their answer.

Using Singleton design pattern in modern C# codebases

If you are working in newer C# codebases and not in the old legacy code codebases, chances are that you are already using the Singleton design pattern.

How?

Through dependency injection.

Many popular dependency injection frameworks have a way of defining an instance of a class to be a single instance. For example:

  • .Net Core: services.AddSingleton<IWorker, Worker>();
  • Autofac: builder.RegisterType<Worker>().SingleInstance();
  • Ninject: kernel.Bind<Worker>().ToSelf().InSingletonScope();

As you can see, the notion of having a single instance has not disappeared. Instead, the Singleton pattern has evolved to be used in modern codebases.

Conclusion

The Singleton design pattern is helpful in many ways and is quite flexible if appropriately used. It is widely used in situations where one and only one instance of a class is required. The class that implements the Singleton pattern is called the Singleton class, and the object of such a class is called the Singleton object. In this article, you have learned about 6 different ways of implementing the Singleton pattern in C#, along with its benefits and use cases.

If you want to learn more about design patterns, check the separate in-depth article about design patterns in C#.

Recent Posts