11
JulSingleton Design Pattern: An Easy Learning
Singleton Design Pattern
The Singleton Design Pattern is a key notion in C# object-oriented programming. It assures that a class has just one instance and gives a global interface to that instance. This can be especially helpful in situations where only one item is required to coordinate operations throughout the system.
In this C# tutorial, we'll look at the Singleton Design Pattern. What is the singleton design pattern in C#? And when should I use it? We'll also look at the Singleton Design Pattern in C#, with examples. So, let us begin by addressing the question, "What is the Singleton Design Pattern?"
What is the Singleton Design Pattern?
- The Singleton Design Pattern guarantees that a class has only one instance and gives a global access point to it.
- It is widely used to manage shared resources, such as configuration settings and logging.
- Consider a corporation that requires a single point of communication for all personnel; the CEO serves as this single point.
- In a software system, a Singleton could be used to manage a configuration file, guaranteeing that all program components use the same settings consistently.
Singleton Pattern - UML Diagram
The UML class diagram for the implementation of the Singleton design pattern is given below:

When should you use the Singleton Design Pattern in C#?
Here's a brief overview of when to apply the Singleton Design Pattern in real-time applications:
- Configuration management: Process of ensuring that all configuration settings are consistent.
- Logging Services: To unify and organize logging throughout the application.
- Database Connections: You can manage and reuse a single database connection or connection pool.
- Cache management: Involves keeping a single cache instance to ensure consistent data retrieval.
- Thread Pool Management: Deals with the efficient management and reuse of threads.
There are various approaches to implementing the Singleton Pattern in C#.
- Basic Singleton Implementation
- Thread-Safe Singleton Implementation
- Lazy Initialization Singleton Implementation
- Eager Initialization Singleton Implementation
1. Basic Singleton Implementation
- In the Basic Singleton Implementation, a private static variable stores the class's sole instance.
- The private constructor restricts external instantiation.
- The public static Instance property allows access to a single instance, which is created if it does not already exist.
- This method ensures that just one instance of the class is produced and utilized throughout the application.
Example
using System;
// LogManager class (Singleton implementation)
public class LogManager
{
private static LogManager _instance;
private static readonly object _lock = new object();
private LogManager() { }
public static LogManager Instance
{
get
{
if (_instance == null)
{
lock (_lock)
{
if (_instance == null)
{
_instance = new LogManager();
}
}
}
return _instance;
}
}
public void Log(string message)
{
Console.WriteLine($"Log: {message}");
}
}
// Main class
public class Program
{
public static void Main(string[] args)
{
LogManager logger = LogManager.Instance;
logger.Log("This is a log message!");
logger.Log("Singleton pattern in action.");
}
}
# Singleton pattern in Python
class LogManager:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super(LogManager, cls).__new__(cls)
return cls._instance
def log(self, message):
print(f"Log: {message}")
# Main function
if __name__ == "__main__":
logger = LogManager()
logger.log("This is a log message!")
logger.log("Singleton pattern in action.")
public class LogManager {
private static LogManager instance;
private LogManager() {}
public static synchronized LogManager getInstance() {
if (instance == null) {
instance = new LogManager();
}
return instance;
}
public void log(String message) {
System.out.println("Log: " + message);
}
public static void main(String[] args) {
LogManager logger = LogManager.getInstance();
logger.log("This is a log message!");
logger.log("Singleton pattern in action.");
}
}
class LogManager {
constructor() {
if (LogManager.instance == null) {
LogManager.instance = this;
}
return LogManager.instance;
}
log(message) {
console.log(`Log: ${message}`);
}
}
// Main
const logger = new LogManager();
logger.log("This is a log message!");
logger.log("Singleton pattern in action.");
class LogManager {
private static instance: LogManager;
private constructor() {}
public static getInstance(): LogManager {
if (!LogManager.instance) {
LogManager.instance = new LogManager();
}
return LogManager.instance;
}
public log(message: string): void {
console.log(`Log: ${message}`);
}
}
// Main
const logger = LogManager.getInstance();
logger.log("This is a log message!");
logger.log("Singleton pattern in action.");
Explanation
This code creates a LogManager class that follows the Singleton pattern to ensure that only one instance exists. This is accomplished by the use of a private static variable _instance and a private constructor, as well as a public static Instance property that provides access to the single instance. The Log method writes messages to the terminal.
Output
Log: This is a log message!
Log: Singleton pattern in action.
2. Thread-Safe Singleton Implementation
- The Thread-Safe Singleton Implementation guarantees that just one instance of a class is produced, even in a multithreaded environment.
- It employs a lock statement to synchronize access to the instance generation process, preventing other threads from creating duplicate instances.
- This technique ensures that only one instance is initialized while allowing for secure concurrent access.
Example
using System;
using System.Threading;
public class ConfigurationManager
{
private static ConfigurationManager _instance;
private static readonly object _lock = new object();
private ConfigurationManager()
{
Console.WriteLine("ConfigurationManager initialized.");
}
public static ConfigurationManager Instance
{
get
{
if (_instance == null)
{
lock (_lock)
{
if (_instance == null)
{
_instance = new ConfigurationManager();
}
}
}
return _instance;
}
}
public string GetSetting(string key)
{
return $"Value for {key}";
}
}
public class Program
{
public static void Main()
{
var thread1 = new Thread(() =>
{
var config1 = ConfigurationManager.Instance;
Console.WriteLine(config1.GetSetting("DatabaseConnection"));
});
var thread2 = new Thread(() =>
{
var config2 = ConfigurationManager.Instance;
Console.WriteLine(config2.GetSetting("ApiKey"));
});
thread1.Start();
thread2.Start();
thread1.Join();
thread2.Join();
}
}
import threading
class ConfigurationManager:
_instance = None
_lock = threading.Lock()
# Private constructor to prevent instantiation from outside
def __init__(self):
if ConfigurationManager._instance is not None:
raise Exception("This class is a singleton!")
print("ConfigurationManager initialized.")
@classmethod
def get_instance(cls):
if cls._instance is None:
with cls._lock:
if cls._instance is None:
cls._instance = ConfigurationManager()
return cls._instance
def get_setting(self, key):
return f"Value for {key}"
def thread_function(key):
config = ConfigurationManager.get_instance()
print(config.get_setting(key))
if __name__ == "__main__":
# Create multiple threads to access ConfigurationManager
thread1 = threading.Thread(target=thread_function, args=("DatabaseConnection",))
thread2 = threading.Thread(target=thread_function, args=("ApiKey",))
thread1.start()
thread2.start()
thread1.join()
thread2.join()
public class ConfigurationManager {
private static ConfigurationManager instance;
private ConfigurationManager() {
System.out.println("ConfigurationManager initialized.");
}
public static ConfigurationManager getInstance() {
if (instance == null) {
synchronized (ConfigurationManager.class) {
if (instance == null) {
instance = new ConfigurationManager();
}
}
}
return instance;
}
public String getSetting(String key) {
return "Value for " + key;
}
}
public class Main {
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
ConfigurationManager config1 = ConfigurationManager.getInstance();
System.out.println(config1.getSetting("DatabaseConnection"));
});
Thread thread2 = new Thread(() -> {
ConfigurationManager config2 = ConfigurationManager.getInstance();
System.out.println(config2.getSetting("ApiKey"));
});
thread1.start();
thread2.start();
}
}
// JavaScript equivalent of the Singleton pattern code goes here
// TypeScript equivalent of the Singleton pattern code goes here
Explanation
Output
ConfigurationManager initialized.
Value for DatabaseConnection
Value for ApiKey
3. Lazy Initialization Singleton Implementation
- In C#, the Lazy Initialization Singleton Pattern makes sure that the class instance is created only when necessary.
- This is accomplished by utilizing a static variable to store the instance and a static method to manage its creation.
- The Lazy<T> class creates a thread-safe instance only when accessed for the first time. This method reduces redundant startup and boosts speed.
Example
using System;
public class Logger
{
// Private static instance of the Logger class, initialized lazily
private static readonly Lazy<Logger> _instance = new Lazy<Logger>(() => new Logger());
// Private constructor to prevent direct instantiation
private Logger()
{
// Initialize resources or setup if needed
}
// Public static property to access the single instance
public static Logger Instance => _instance.Value;
// Method to log messages
public void Log(string message)
{
Console.WriteLine($"Log entry: {message}");
}
}
class Program
{
static void Main()
{
// Access the Logger instance
Logger logger = Logger.Instance;
// Use the Logger to log a message
logger.Log("This is a singleton logger message.");
}
}
Explanation
Output
Log entry: This is a singleton logger message.
4. Eager Initialization Singleton Implementation
- In C#, eager initialization for the Singleton pattern creates a single instance of the class during class loading.
- This is accomplished by declaring a static, read-only instance of the class that is initialized instantly.
- This approach assures thread safety and simplicity, but it may result in increased memory usage if the instance is generated when it is not required.
Example
using System;
public class ConfigurationManager
{
// Private static readonly instance of the ConfigurationManager class, eagerly initialized
private static readonly ConfigurationManager instance = new ConfigurationManager();
// Property to store configuration settings
public string Configuration { get; private set; }
// Private constructor ensures that the class cannot be instantiated from outside
private ConfigurationManager()
{
// Load configuration settings (e.g., from a file or database)
Configuration = LoadConfiguration();
}
// Public static property to access the single instance
public static ConfigurationManager Instance => instance;
// Private method to simulate loading configuration settings
private string LoadConfiguration()
{
// Simulate loading configuration data
return "Configuration data loaded.";
}
}
class Program
{
static void Main()
{
// Access the ConfigurationManager instance
ConfigurationManager configManager = ConfigurationManager.Instance;
// Display the loaded configuration
Console.WriteLine(configManager.Configuration);
}
}
Explanation
Output
Configuration data loaded.
Advantages of Singleton Design Pattern
The Singleton Design Pattern offers the following advantages:
- Controlled Access to the Sole Instance: Ensures that only one instance of the class is generated, with a single point of access to it.
- Reduced Memory Footprint: Saves memory by not creating numerous instances of a class.
- Consistent Access to Shared Resources: Provides a centralized interface for accessing shared resources such as configuration settings or logging, guaranteeing consistency throughout the program.
- Lazy Initialization: The instance is created only when necessary, potentially improving startup time and resource utilization.
- Global State Management: Used to manage an application's global state, ensuring that all system components have synchronized access to shared data.
Disadvantages of Singleton Design Pattern
The Singleton Design Pattern has a few drawbacks:
- Global State Issues: Because a Singleton provides an international point of access, it might cause hidden dependencies between classes, making the system difficult to comprehend and manage.
- Testing Difficulties: Because singletons have a global state, it isn't easy to separate and test individual components separately.
- Concurrency Issues: In multithreaded systems, maintaining thread-safe access to the Singleton instance can complicate implementation and potentially cause performance problems.
- Tight Coupling: Classes that rely on the Singleton may become strongly tied to it, reducing flexibility and making it more difficult to replace or restructure.
Summary
FAQs
Take our Designpatterns skill challenge to evaluate yourself!

In less than 5 minutes, with our skill challenge, you can identify your knowledge gaps and strengths in a given skill.