Learn about Singleton Pattern - a design pattern that ensures a class has only one instance and provides global access to it. Complete guide with C# examples and real-world use cases.

No table of contents available for this article
Singleton is one of the 5 design patterns in the Creational Design Pattern group - patterns that help us create objects in a smart way. The Singleton Pattern has a simple yet powerful purpose: ensure a class has only one instance throughout the application's lifecycle and provide a global access point to that instance.
Think of it this way: Imagine your company has a safe where important documents are stored. You wouldn't want multiple safes scattered around because it would be chaotic and hard to manage. You need just one safe in a specific location, and everyone in the company knows exactly where it is and how to access it. That's essentially what Singleton Pattern does in software.
When should we use Singleton?
When you need to ensure only one instance of a class exists in the entire application
When you need a centralized access point to a shared resource
When you want to control and limit the number of instances (in this case, exactly 1)
When multiple parts of your application need to work with the same object
Common scenarios:
A single configuration manager that holds all app settings
One logger that records all events
One database connection pool manager
One cache manager for the entire system

During software development, we often face situations where we need an object that exists uniquely and can be accessed from anywhere in our application. But how do we implement this correctly?
Real-world use cases:
Logger System
You need a single logging system to track all events in your application. Having multiple loggers would create confusion - which log file should you check? A single logger ensures all logs go to one place.
Database Connection Pool
Managing database connections is resource-intensive. Creating multiple connection pools would waste memory and database resources. One pool can efficiently manage and reuse connections for the entire application.
Configuration Manager
Your application needs access to configuration settings (API keys, database URLs, feature flags). One configuration object ensures consistency - everyone reads the same settings.
Print Spooler
When multiple users send print jobs, you need one queue manager to organize and process them in order. Multiple spoolers would create chaos.
The wrong approach: Global Variables
You might think: "Easy! I'll just create a global variable like public static final and everyone can access it!"
Why this doesn't work:
❌ Breaks Encapsulation: Anyone can modify or even recreate the object
❌ No initialization control: You can't prevent multiple instances from being created
❌ Poor timing control: The object is created immediately, even if you don't need it yet (wastes resources)
❌ Testing nightmares: Global state makes unit testing extremely difficult
The right solution: Singleton Pattern
Singleton Pattern solves all these problems elegantly while following Object-Oriented Programming principles:
✅ Guarantees only one instance exists
✅ Provides controlled access through a single method
✅ Lazy initialization - creates instance only when needed
✅ Thread-safe implementations available
✅ Maintains encapsulation
Core Components of Singleton:
A properly implemented Singleton class has three essential parts:
3.1. Private Constructor
private Singleton() { }This prevents anyone from creating a new instance using new Singleton(). The constructor is locked down so only the class itself can create instances.
3.2. Private Static Instance Variable
private static Singleton instance = null;This variable holds the one and only instance. It's static (belongs to the class, not individual objects) and private (only accessible within the class).
3.3. Public Static Access Method
public static Singleton GetInstance()
{
if (instance == null)
{
instance = new Singleton();
}
return instance;
}This is the only way to get the instance. It checks if an instance exists; if not, it creates one. If it already exists, it returns that same instance.
How Singleton Works: Step by Step:
Client → call Singleton.getInstance()
↓
[Check instance → NULL?]
↓
Yes → Create new instance
No → Return exist instance
↓
Client receive only same instanceGuarantees single instance: You can be 100% sure that the class has only one instance in the application
Global access point: Easy access to the instance from anywhere
Lazy initialization: Instance is only created when actually needed for the first time, saving resources
Better control: Tightly manages access and usage of shared resources
Reduces namespace pollution: Doesn't create many messy global variables
Violates Single Responsibility Principle (SRP): Singleton manages both instance creation and business logic - doing too many things in one class
Bad design indicator: If components know too much about each other and all depend on Singleton, it's a sign of poor design
Difficult to unit test: Many testing frameworks rely on inheritance to create mock objects, but Singleton with private constructor makes this difficult
Thread-safety issues: In multi-threaded environments, careful handling is needed to avoid creating multiple instances
Tight coupling: Classes using Singleton are tightly bound, making changes difficult
Suitable scenarios:
✔️ Shared Resources
Database connection pool
File manager
Device drivers (printer, scanner...)
✔️ Logger/Logging System
Centralized logging
Application event tracking
✔️ Configuration Manager
System-wide configuration management
Settings, preferences
✔️ Caching
Cache manager
Session storage
✔️ Thread Pool
Thread pool management
✔️ Service Locator
Registry pattern
Dependency Injection container
Other Design Patterns that use Singleton:
Abstract Factory
Builder
Prototype
Facade
⚠️ Note: Don't overuse Singleton! Only use it when you truly need a single instance and global access. In many cases, Dependency Injection is a better choice.
csharp
public sealed class Singleton1
{
// Private constructor prevents external instantiation
private Singleton1()
{
// Initialize properties if needed
}
// Private static variable stores the unique instance
private static Singleton1 instance = null;
// Public property to access the instance
public static Singleton1 Instance
{
get
{
// Create new instance if it doesn't exist
if (instance == null)
{
instance = new Singleton1();
}
return instance;
}
}
}⚠️ Problem: This code is NOT safe in multi-threaded environments. If two threads simultaneously call Instance when instance == null, both might create their own instance.
csharp
public sealed class Singleton2
{
// Private constructor
private Singleton2()
{
// Initialization
}
// Lock object to ensure thread-safety
private static readonly object lockObject = new object();
private static Singleton2 instance = null;
public static Singleton2 Instance
{
get
{
// Lock to ensure only one thread enters this block at a time
lock(lockObject)
{
if (instance == null)
{
instance = new Singleton2();
}
return instance;
}
}
}
}Explanation:
lock(lockObject): Ensures only one thread executes the code inside at a time
If thread A is inside the lock, thread B must wait until A exits the lock
This prevents creating multiple instances
✨ Improvement: Double-Check Locking
csharp
public sealed class Singleton3
{
private Singleton3() { }
private static readonly object lockObject = new object();
private static Singleton3 instance = null;
public static Singleton3 Instance
{
get
{
// First check (no lock) - faster
if (instance == null)
{
lock(lockObject)
{
// Second check (inside lock) - ensures safety
if (instance == null)
{
instance = new Singleton3();
}
}
}
return instance;
}
}
}Benefit: Only locks when truly necessary (first time), subsequent times don't need locking → better performance.
Singleton Pattern is a simple yet extremely useful design pattern when you need to ensure only one instance of a class exists. However, be careful with thread-safety and don't overuse this pattern.
Key Takeaways:
Singleton = 1 unique instance + global access
Private constructor + static instance + getInstance method
Be careful with multi-threading
Consider using Dependency Injection instead of Singleton in some cases
If you found this article helpful, explore more in the Design Patterns Series to enhance your programming skills!
[1] Refactoring.Guru. https://refactoring.guru/design-patterns
[2] Design Patterns for Dummies, Steve Holzner, PhD
[3] Head First Design Patterns, Eric Freeman
[4] Gang of Four Design Patterns 4.0
[5] Dive into Design Patterns