.NET Core C#.NET Design Patterns

Understanding Singleton design pattern

Design Pattern – Singleton
In this blog we discuss and learn about the easy design pattern of all the design patterns – the Singleton pattern. This article is an attempt to provide the new developers / readers with some basic understanding about singleton pattern and its implementation in my words.
Singleton pattern when implemented provides a single instance and global access of the instance to the application using it.
There are two points to note here single instance and global access. Let’s talk about them.
Single instance. – instance (object) are created when we new up a class, so when we want to restrict instance creation we need to restrict access to the constructor of the class – solution is make the constructor private. No making constructor private will completely stop instance creation from outside, but the constructor can be called from the class itself and can create instance of the class. Solution – Using a property instance or a method instance (note: make the property or method public static so they are accessible outside the class.
Global access – since the property / method is static, other objects can access this which return a reference to the instance (object) of the class.
Note: The class is marked sealed to prevent derivation, which could add instances. We cannot have a static class as we need at least one instance of the class – we cannot create any instance of a static class

public sealed class SingletonMath
{
private SingletonMath()
{

}

private static SingletonMath instance = null;

public static SingletonMath Instance
{
get
{
if (instance == null)
{

instance = new SingletonMath();
}

return instance;

}

}
}

Using Static Method

public sealed class SingletonMethod
{
private SingletonMethod()
{

}

private static SingletonMethod instance = null;

public static SingletonMethod GethInstance()
{
if (instance == null)
{
instance = new SingletonMethod();
}

return instance;
}
}

Hello Multi-Threading
There is a problem with this class. it is not thread safe. So to make it thread safe, we need to make sure that the instantiation code can only be accessed by only a single thread at any given time. So lets do that and introduce a lock in our class to guard the GetInstance calls.
public sealed class SingletonMethod
{
private SingletonMethod()
{

}

private static SingletonMethod instance = null;
private static readonly object _lock = new object();

public static SingletonMethod GethInstance()
{
lock (_lock)
{
// create the instance only if the instance is null
if (instance == null)
{
instance = new SingletonMethod();
}
}

// other wise return the already existing instance
return instance;
}
}

Now the instance retrieval code can only be accessed by one single thread at any given time. But this incorporates a performance problem. Our idea was to not let multiple threads be able to create multiple instances so we needed to guard the instance creation part in lock. What we have done instead is that we have guarded the complete method with lock. Which would mean that the lock will be acquired even when the instance has been created and it just needs to be returned. So to circumvent this problem, we need to guard only the instance creation part under lock and not the instance return part.

public sealed class SingletonMethod
{
private SingletonMethod()
{

}

private static SingletonMethod instance = null;
private static readonly object _lock = new object();

public static SingletonMethod GethInstance()
{
if (instance == null)
{
lock (_lock)
{
// create the instance only if the instance is null
if (instance == null)
{
instance = new SingletonMethod();
}
}
}

// other wise return the already existing instance
return instance;
}
}

Now what we have is a class that will acquire lock only for first instance creation and rest of the time it will return the already created instance.

Using C# specification
The static constructor for a class executes at most once in a given application domain. The execution of a static constructor is triggered by the first of the following events to occur within an application domain:
• An instance of the class is created.
• Any of the static members of the class are referenced.
So a better solution for creation and instance for a singleton class is to create it in the static constructor of the class. So it will be execute at most once and the instance will be created.

public sealed class SingletonMethod
{
private SingletonMethod()
{

}

static SingletonMethod()
{
instance = new SingletonMethod();
}

private static SingletonMethod instance = null;

public static SingletonMethod GethInstance()
{

// other wise return the already existing instance
return instance;
}
}

Another way is creating a nested class in the singleton class and then instantiate object.

public sealed class SingletonMethod
{
private SingletonMethod()
{
}

public SingletonMethod Instance
{
get { return Nested.instance; }
}

private class Nested
{
static Nested()
{
}

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

So what we have done here is that we have moved the instantiation in the static constructor which will get called when the GetInstance method will be called. Static constructor will create the instance and that instance will get returned from by the GetInstance method.
The instance field has been marked as read-only so that it can only be instantiated during static initialization. And now we have a fairly usable singleton class that is thread safe without the performance overhead of locks.
Note: One might argue that the in our last version, the initialization is not lazy as if this class has any other static member then the instance will get created even if it is not requested. There is one more version of singleton possible where this can be circumvented using either nested class or Lazy. But I am not going to discuss them here. In most cases, a singleton will not have other static methods. If we find ourselves in a scenario where we have a singleton that also has other static methods, perhaps we are violating Single Responsibility Principle and should revisit our class design again.

For more advanced readers, I recommend reading this blog for much more detailed explanation of this pattern: C# in Depth: Implementing the Singleton Pattern[^]

Regards,
Hussain Patel

Leave a Reply