Singleton Pattern in C#

kalkwst

Kostas Kalafatis

Posted on September 6, 2022

Singleton Pattern in C#

Originally posted here

The Singleton is a creational design pattern that lets us ensure that a class has only one instance while providing a global access point to this instance.

The Singleton pattern gives a standard means of providing a specific instance of an element all across the lifecycle of an application. Put it in another way; if the application isn't restarted, the instance remains the same.

You can find the example code of this post, on GitHub

Conceptualizing the Problem

The Singleton pattern solves two problems at the same time.

Ensuring that a class has just a single instance

Imagine that we are implementing a new caching solution. We have a class named CachingProvider that contains a dictionary. In this dictionary, we will store all of our cache entries.

After a while, we decide to create a new instance of the CachingProvider. Instead of receiving a fresh object, we need to get the one already created. This behaviour is impossible to implement with a regular constructor. All constructor calls must always return a new object by design.

Providing a Global Access Point to the instance

While global variables can come in quite handy, they're also very unsafe since any code can potentially overwrite the contents of those variables and crash the application.

Just like global variables, the Singleton pattern lets us access some objects from anywhere in the application. However, it also protects that instance from being overwritten by any other functions.

All implementations of the Singleton pattern have these two steps in common:

  • Make the default constructor private, to prevent other objects from using the new operator with the Singleton class.
  • Create a static creation method that acts as a constructor. Under the hood, this method calls the private constructor to create an object and saves it in a static field. All following calls to this method return the cached object.

If the code has access to the Singleton class, then it's able to call Singleton's static method. So wherever that method is called, the same object is always returned.

Structuring the Singleton Pattern

The following diagram demonstrates how the Singleton pattern works.

Singleton High Level Diagram

  1. The Singleton class defines exactly one instance of itself, and that instance is globally accessible.

In its base implementation, the Singleton pattern has a single participant:

  • Singleton: The Singleton class declares the static GetInstance that returns the same instance of its class.

Singleton Class Diagram

To demonstrate how the Singleton pattern works, we are going to think about the little bell that sits on the counter of restaurants.

The thing about that bell is that there's probably only one; the sound is used to notify the servers that the next order is at the window and needs to be taken to the tables.

Since there's only ever one bell, we can model it as a Singleton.



namespace Singleton.NaiveSingleton
{
    /// <summary>
    /// Naive Singleton
    /// </summary>
    public sealed class Bell
    {
        private static Bell instance = null;

        // Private constructor
        private Bell() { }

        // Property to access the Instance
        public static Bell Instance
        {
            get
            {
                // Check if the Instance already exists
                if (instance == null)
                    instance = new Bell();

                return instance;
            }
        }

        public void Ring()
        {
            Console.WriteLine("Ding! Order Up!");
        }
    }
}


Enter fullscreen mode Exit fullscreen mode

Notice that the Bell class has a private constructor. This is to ensure it can never be instantiated, and can only be accessed through the Instance property.

Let's set up the Main method. First, we need to define a new bell variable. To create an instance of the NaiveSingleton class, we need to call its Instance property.



using Singleton.NaiveSingleton;

Bell bell = Bell.Instance;
bell.Ring();


Enter fullscreen mode Exit fullscreen mode

And of course, the output of the above will be:

Singleton Output

Implementations of the Singleton Pattern

There are various ways to implement the Singleton design pattern.

Naive Singleton (No thread safety)



namespace Singleton.NaiveSingleton
{
    /// <summary>
    /// Naive Singleton
    /// </summary>
    public sealed class NaiveSingleton
    {
        private static NaiveSingleton instance = null;

        // Private constructor
        private NaiveSingleton() { }

        // Property to access the Instance
        public static NaiveSingleton Instance
        {
            get
            {
                // Check if the Instance already exists
                if (instance == null)
                    instance = new NaiveSingleton();

                return instance;
            }
        }

        public void Ring()
        {
            Console.WriteLine("Ding! Order Up!");
        }
    }
}


Enter fullscreen mode Exit fullscreen mode

This implementation does not ensure thread safety as two different threads can read the instance variable as null and create different instances, thus violating the Singleton pattern's assurance of a single global instance.

Simple Thread Safe Singleton



namespace Singleton.SingleThreadSafe
{
    public sealed class SingleThreadSafe
    {
        private static SingleThreadSafe instance = null;
        private static object lockpad = new object();

        // Private Constructor
        private SingleThreadSafe() { }

        // Property to access the Instance
        public static SingleThreadSafe Instance
        {
            get
            {
                // locking the shared object
                lock (lockpad)
                {
                    if(instance == null)
                        instance = new SingleThreadSafe();

                    return instance;
                }
            }
        }

        public void Ring()
        {
            Console.WriteLine("Ding! Order Up!");
        }
    }
}



Enter fullscreen mode Exit fullscreen mode

The above class provides some thread safety as the thread locks the shared object before checking for the instance. Locking the padlock ensures that all reads occur after the lock acquire, and unlocking the padlock ensures that all writes occur before the lock release.

It also ensures that only one thread will create an instance, as only one thread can acquire the lock at a time. By the time a second thread acquires the lock and enters the contested code, the first thread will have already created the instance.

The performance of the application will suffer, since a padlock needs to be acquired and released every time a class refers to the instance.

Double-check locking Singleton



namespace Singleton.DoubleCheckLocking
{
    public sealed class DoubleCheckLocking
    {
        private static DoubleCheckLocking instance = null;
        private static object lockpad = new object();

        // Private Constructor
        private DoubleCheckLocking() { }

        public static DoubleCheckLocking Instance
        {
            get
            {
                // First check
                if(instance == null)
                {
                    // Locking the padlock
                    lock (lockpad)
                    {
                        if(instance == null)
                            instance = new DoubleCheckLocking();
                    }
                }

                return instance;
            }
        }
        public void Ring()
        {
            Console.WriteLine("Ding! Order Up!");
        }
    }
}


Enter fullscreen mode Exit fullscreen mode

This implementation attempts thread safety without locking the lockpad every time the instance is called. This variant of the Singleton pattern has some downsides though.

We have to use this implementation as is, since any significant change of the above, is likely to impact either performance or correctness.

No Lock Thread-Safe Singleton



namespace Singleton.NoLockThreadSafe
{
    public sealed class NoLockThreadSafe
    {
        private static NoLockThreadSafe instance = new NoLockThreadSafe();

        // Explicit static constructor to force the C#
        // compiler not to mark the type as beforefieldinit
        static NoLockThreadSafe() { }

        private NoLockThreadSafe(){}

        public static NoLockThreadSafe Instance
        {
            get { return instance; }
        }

        public void Ring()
        {
            Console.WriteLine("Ding! Order Up!");
        }
    }
}


Enter fullscreen mode Exit fullscreen mode

This is an extremely simple implementation of a thread-safe, lazy Singleton. Static constructors in C# are specified to execute only when an instance of the class is created or a static member is referenced. Static constructors also are executed only once per AppDomain.

Given that the check for the type being newly constructed is performed whatever else happens, it is faster than adding extra locking checks.

However, it's not as lazy as other implementations. In particular, if we have static members other than Instance, the first reference to any of them will involve creating the instance.

Fully Lazy Instantiation



namespace Singleton.FullyLazyInstantiation
{
    public sealed class FullyLazyInstantiation
    {
        private FullyLazyInstantiation() { }

        public static FullyLazyInstantiation Instance { get { return Nested.Instance; } }

        private class Nested
        {
            // Explicit static constructor to force the C#
            // compiler not to mark the type as beforefieldinit
            static Nested() { }

            internal static readonly FullyLazyInstantiation Instance = new FullyLazyInstantiation();
        }

        public void Ring()
        {
            Console.WriteLine("Ding! Order Up!");
        }
    }
}


Enter fullscreen mode Exit fullscreen mode

In this implementation, instantiation is triggered by the first reference to the static member of the nested class. This only occurs by calling Instance.

This means that the implementation is fully lazy, but has all the performance benefits of the previous ones. Note that although the nested class has access to the enclosing class's private members, the reverse is not true. That doesn't raise any other problems, though, as the class itself is private.

.NET 4's Lazy type



namespace Singleton.DotNetLazy
{
    public sealed class DotNetLazy
    {
        private static readonly Lazy<DotNetLazy> lazy =
            new Lazy<DotNetLazy>(() => new DotNetLazy());

        public static DotNetLazy Instance => lazy.Value;

        private DotNetLazy() { }

        public void Ring()
        {
            Console.WriteLine("Ding! Order Up!");
        }
    }
}


Enter fullscreen mode Exit fullscreen mode

If we are using .NET 4 or higher (which we probably do), we can use the System.Lazy type to make the laziness simple. All we need to do is to pass a delegate to the constructor which calls the Singleton constructor.

Pros and Cons of Singleton Pattern

✔ We can be sure that a class has only a single instance ❌The Singleton pattern can mask bad design, for instance, when components of an application know too much about each other
✔ We gain a global access point to that instance ❌The pattern requires special treatment in multithreaded environments
✔ The singleton can be initialized only when it's requested for the first time ❌ It may be difficult to unit test the client code of the Singleton because many test frameworks rely on inheritance when producing mock objects

Relations with Other Patterns

  • A Facade class can often be transformed to a Singleton since a single Facade object is sufficient in most cases.
  • Abstract Factories, Builders and Prototypes can all be implemented as Singletons.

Final Thoughts

In this article, we have discussed what is the Singleton pattern, when to use it and what are the pros and cons of using this design pattern. We then examined different implementations for the Singleton patterns and how the Singleton pattern relates to other classic design patterns.

The Singleton design pattern is helpful in many ways and is quite flexible if appropriately used. However, it's worth noting that the Singleton pattern, along with the rest of the design patterns presented by the Gang of Four, is not a panacea or a be-all-end-all solution when designing an application. Once again it's up to the engineers to consider when to use a specific pattern. After all these patterns are useful when used as a precision tool, not a sledgehammer.

💖 💪 🙅 🚩
kalkwst
Kostas Kalafatis

Posted on September 6, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related