The Singleton Design Pattern is one of the simplest yet most commonly used patterns in software engineering. Classified under the Creational Design Patterns in the Gang of Four (GoF) catalog, its primary goal is to ensure that a class has only one instance while providing a global point of access to that instance. Here this post, we will explore what is the Singleton, Singleton Design Pattern and its implementantion.
The Singleton Design Pattern: A Complete Guide
Getting Started
In software engineering, creating and managing objects efficiently is essential for building scalable and maintainable systems. Among the various approaches defined in the Creational Design Patterns category, the Singleton Design Pattern stands out as one of the simplest yet most influential. The primary purpose of the Singleton pattern is to ensure that a class has only one instance throughout the lifetime of an application and to provide a global point of access to that instance.
This pattern is particularly useful in scenarios where exactly one object is needed to coordinate actions or manage shared resources—for example, configuration managers, logging services, database connection handlers, or cache controllers. By restricting object creation and centralizing control, the Singleton pattern helps prevent inconsistent states, reduces unnecessary duplication, and ensures a unified behavior across the system.
Despite its benefits, the Singleton pattern must be used thoughtfully, as overuse can introduce challenges such as hidden dependencies, tight coupling, and difficulties in unit testing. When applied correctly, however, it offers a clean and effective solution for managing globally shared resources in both small-scale and enterprise-level applications.
What Is a Singleton?
A Singleton restricts the instantiation of a class to a single object. This means that no matter how many times you try to create an instance, the system will always return the same instance.
Key Characteristics- Single instance throughout the program.
- Controlled access to that instance.
- Lazy initialization (optional): instance is created only when needed.
- Global availability.
Example
public class Singleton {
// The single instance (lazy-loaded)
private static Singleton instance;
// Private constructor to prevent external instantiation
private Singleton() {}
// Public method to provide global access
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
What makes this class a Singleton Class?
- Private constructor prevents new Singleton().
- Static variable holds the single instance.
- Static method controls access.
Why Use the Singleton Pattern?
Singletons are useful when:- Exactly one instance of a class is needed to coordinate actions across a system.
- A central shared resource must be controlled, such as:
- Logging services
- Configuration managers
- Thread pools
- Cache handlers
- Database connection managers
Real-World Analogies
- A country has only one president.
- A computer can have only one print spooler.
- A system clock is a single source of time.
Singleton Pattern in C# Implementation
Here are clean and commonly used C# implementations of the Singleton Design Pattern, ranging from basic to advanced, thread-safe versions.
Basic Singleton (Lazy Initialization)
public class Singleton
{
private static Singleton _instance;
private Singleton() { }
public static Singleton Instance
{
get
{
if (_instance == null)
_instance = new Singleton();
return _instance;
}
}
}
Note: Not suitable for multithreaded applications.
Thread-Safe Singleton (Using lock)
public class Singleton
{
private static Singleton _instance;
private static readonly object _lock = new object();
private Singleton() { }
public static Singleton Instance
{
get
{
lock (_lock)
{
if (_instance == null)
_instance = new Singleton();
}
return _instance;
}
}
}
Note:- This version ensures that only one thread can create the instance and is suitable for multithreaded environments, but it requires locking every time the instance is accessed.
Double-Checked Locking (Optimized Thread-Safe)
public class Singleton
{
private static Singleton _instance;
private static readonly object _lock = new object();
private Singleton() { }
public static Singleton Instance
{
get
{
if (_instance == null)
{
lock (_lock)
{
if (_instance == null)
_instance = new Singleton();
}
}
return _instance;
}
}
}
Note:- This approach is thread-safe and faster than the previous one, as locking occurs only during instance creation.
Thread-Safe + Lazy Loading (Recommended: C# Lazy<T>**)
public class Singleton
{
private static readonly Lazy<Singleton> _instance =
new Lazy<Singleton>(() => new Singleton());
private Singleton() { }
public static Singleton Instance => _instance.Value;
}
| Version | Thread-Safe | Lazy | Recommended |
|---|---|---|---|
| Basic | ❌ | ✔ | ❌ |
| lock | ✔ | ✔ | ◉ |
| Double-Checked | ✔ | ✔ | ✔ |
Lazy<T> |
✔ | ✔ | ⭐ Best option |
| Eager | ✔ | ❌ | When instance must exist early |
Advantages of Singleton Pattern
- Ensures a single instance across the system
- Global access simplifies resource sharing
- Saves memory by avoiding redundant objects
- Useful for centralized configuration or control
Disadvantages of Singleton Pattern
- Harder to test due to hidden dependencies
- Difficult to use in multithreaded or distributed environments
- Can lead to tight coupling
Summary
The Singleton Pattern is simple and powerful, but must be used judiciously. When applied correctly, it provides clean, centralized control over system-wide resources.
Thanks