What is Singleton Design Pattern?
Singleton design pattern in C# is one of the most popular design patterns. In this pattern, a class has only one instance in the program that provides a global point of access to it. In other words, a singleton is a class that allows only a single instance of itself to be created and usually gives simple access to that instance.
There are various ways to implement a singleton pattern in C#. The following are the common characteristics of a singleton pattern.
- Private and parameterless single constructor
- Sealed class.
- Static variable to hold a reference to the single created instance
- A public and static way of getting the reference to the created instance.
Advantages of Singleton Design Pattern
The advantages of a Singleton Pattern are,
- Singleton pattern can implement interfaces.
- Can be lazy-loaded and has Static Initialization.
- It helps to hide dependencies.
- It provides a single point of access to a particular instance, so it is easy to maintain.
Disadvantages of Singleton Design Pattern
The disadvantages of a Singleton Pattern are,
- Unit testing is a bit difficult as it introduces a global state into an application
- Reduces the potential for parallelism within a program by locking.
Singleton class vs. Static methods
The following compares Singleton class vs. Static methods,
- A Static Class cannot be extended whereas a singleton class can be extended.
- A Static Class cannot be initialized whereas a singleton class can be.
- A Static class is loaded automatically by the CLR when the program containing the class is loaded.
How to Implement Singleton Pattern in C# code
There are several ways to implement a Singleton Pattern in C#.
- No Thread Safe Singleton.
- Thread-Safety Singleton.
- Thread-Safety Singleton using Double-Check Locking.
- Thread-safe without a lock.
- Using .NET 4's Lazy<T> type.
No Thread Safe Singleton
Explanation of the following code,
- The following code is not thread-safe.
- Two different threads could both have evaluated the test (if instance == null) and found it to be true, then both create instances, which violates the singleton pattern.
public sealed class Singleton1 {
private Singleton1() {}
private static Singleton1 instance = null;
public static Singleton1 Instance {
get {
if (instance == null) {
instance = new Singleton1();
}
return instance;
}
}
}
Thread Safety Singleton
Explanation of the following code,
- The following code is thread-safe.
- In the code, the thread is locked on a shared object and checks whether an instance has been created or not. It takes care of the memory barrier issue and ensures that only one thread will create an instance. For example: Since only one thread can be in that part of the code at a time, by the time the second thread enters it, the first thread will have created the instance, so the expression will evaluate as false.
- The biggest problem with this is performance; performance suffers since a lock is required every time an instance is requested.
public sealed class Singleton2 {
Singleton2() {}
private static readonly object lock = new object();
private static Singleton2 instance = null;
public static Singleton2 Instance {
get {
lock(lock) {
if (instance == null) {
instance = new Singleton2();
}
return instance;
}
}
}
}
Thread Safety Singleton using Double-Check Locking
Explanation of the following code,
In the following code, the thread is locked on a shared object and checks whether an instance has been created or not with double checking.
public sealed class Singleton3 {
Singleton3() {}
private static readonly object lock = new object();
private static Singleton3 instance = null;
public static Singleton3 Instance {
get {
if (instance == null) {
lock(lock) {
if (instance == null) {
instance = new Singleton3();
}
}
}
return instance;
}
}
}
Thread Safe Singleton without using locks and no lazy instantiation
Explanation of the following code:
- The preceding implementation looks like a very simple code.
- This type of implementation has a static constructor, so it executes only once per Application Domain.
- It is not as lazy as the other implementation.
public sealed class Singleton4
{
private static readonly Singleton4 instance = new Singleton4();
static Singleton4()
{
}
private Singleton4()
{
}
public static Singleton4 Instance
{
get
{
return instance;
}
}
}
Using .NET 4's Lazy<T> type
Explanation of the following code:
- If you are using .NET 4 or higher then you can use the System.Lazy<T> type to make the laziness really simple.
- You can pass a delegate to the constructor that calls the Singleton constructor, which is done most easily with a lambda expression.
- Allows you to check whether or not the instance has been created with the IsValueCreated property.
public sealed class Singleton5
{
private Singleton5()
{
}
private static readonly Lazy<Singleton5> lazy = new Lazy<Singleton5>(() => new Singleton5());
public static Singleton5 Instance
{
get
{
return lazy.Value;
}
}
}
Hope you liked the article. Please let me know the feedback in the comments section.