Singleton Design Pattern

Singleton Design Pattern

29 Jul 2024
Intermediate
206K Views
14 min read
Learn with an interactive course and practical hands-on labs

⭐ .NET Design Patterns Course: Design Patterns in C# Online Training

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:
Singleton Pattern - UML Diagram

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.
How to Implement Singleton Pattern in C# code

There are various approaches to implementing the Singleton Pattern in C#.

  1. Basic Singleton Implementation
  2. Thread-Safe Singleton Implementation
  3. Lazy Initialization Singleton Implementation
  4. 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

public class LogManager
{
    // Private static instance of the class
    private static LogManager _instance;

    // Private constructor to prevent instantiation from outside
    private LogManager() { }

    // Public static method to get the instance of the class
    public static LogManager Instance
    {
        get
        {
            // Create an instance if it doesn't exist
            if (_instance == null)
            {
                _instance = new LogManager();
            }
            return _instance;
        }
    }

    // Example method for logging messages
    public void Log(string message)
    {
        // Implementation for logging messages
        Console.WriteLine($"Log: {message}");
    }
}    

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: First message
Log: Second message
True

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 constructor to prevent instantiation from outside
    private ConfigurationManager()
    {
        // Simulate loading configuration settings
        Console.WriteLine("ConfigurationManager initialized.");
    }

    // Public static method to get the instance of the class
    public static ConfigurationManager Instance
    {
        get
        {
            // First check without locking (performance optimization)
            if (_instance == null)
            {
                lock (_lock)
                {
                    // Double-check if instance is still null
                    if (_instance == null)
                    {
                        _instance = new ConfigurationManager();
                    }
                }
            }
            return _instance;
        }
    }

    // Example method for getting a configuration value
    public string GetSetting(string key)
    {
        // Implementation for retrieving settings
        return $"Value for {key}";
    }
}

public class Program
{
    public static void Main()
    {
        // Create multiple threads to access ConfigurationManager
        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();
    }
}

Explanation

This example shows a thread-safe Singleton pattern in C#. The ConfigurationManager class uses double-checked locking to ensure that only one instance is created, even when there is concurrent access. The Program class generates two threads that access the Configuration Manager. Instance, indicating that the singleton instance is only shared and initialized once. The output confirms the singleton behavior by demonstrating that startup happens only once and settings are accurately received.

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 _instance = new Lazy(() => 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

The Logger class uses the sluggish<T> class to implement the Singleton pattern through sluggish initialization. It ensures that only one instance of Logger is created and accessed via the Instance property. The Log method allows you to log messages, and this instance is thread-safe and only created when needed. The Program class explains how to obtain and use the Logger instance to log a message.

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

public class ConfigurationManager
{
    private static readonly ConfigurationManager instance = new ConfigurationManager();
    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 property to access the single instance
    public static ConfigurationManager Instance => instance;

    // Method to simulate loading configuration settings
    private string LoadConfiguration()
    {
        // Simulate loading configuration data
        return "Configuration data loaded.";
    }
}

Explanation

This ConfigurationManager class uses eager initialization to generate a single, globally available instance at class load time. The instance is created using a private constructor that loads configuration settings immediately. The Instance property gives you access to this pre-created instance, which ensures that configuration data is available throughout the application's lifecycle.

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
The Singleton Design Pattern guarantees that a class has a single, globally accessible instance. In C#, it can be implemented using basic methods, thread-safe ways with double-checked locking, lazy initialization with Lazy<T>, or eager initialization by constructing the instance during class load time. Each technique balances performance, memory utilization, and initialization timing.

FAQs

The Singleton design pattern's main aim is to ensure that a class has only one instance and gives a global point of access to it.

The Singleton design pattern has disadvantages, including difficult unit testing due to tight coupling and potential concerns with scalability and concurrency in multi-threaded systems.

A real-world example of employing the Singleton design pattern is to manage a global configuration settings object that is accessed throughout an application to ensure consistency and avoid duplicates.

The singleton design restricts class instantiation and ensures that only one instance exists in the Java Virtual Machine. The singleton class must provide a global access point for retrieving the class object. The singleton design is used for logging, driver objects, caching, and thread pooling.
Share Article
About Author
Shailendra Chauhan (Microsoft MVP, Founder & CEO at Scholarhat by DotNetTricks)

Shailendra Chauhan is the Founder and CEO at ScholarHat by DotNetTricks which is a brand when it comes to e-Learning. He provides training and consultation over an array of technologies like Cloud, .NET, Angular, React, Node, Microservices, Containers and Mobile Apps development. He has been awarded Microsoft MVP 9th time in a row (2016-2024). He has changed many lives with his writings and unique training programs. He has a number of most sought-after books to his name which has helped job aspirants in cracking tough interviews with ease.
Accept cookies & close this