Showing posts with label Creational Design Patterns – Singleton Pattern. Show all posts
Showing posts with label Creational Design Patterns – Singleton Pattern. Show all posts

Saturday, June 27, 2026

Singleton Design Pattern

Part 2.1 – Singleton Design Pattern

Series: Design Patterns in C# and ASP.NET Core

Part: 2.1 – Creational Design Patterns – Singleton Pattern

Target Audience: Beginners to Advanced .NET Developers


Table of Contents

  1. Introduction

  2. What is the Singleton Pattern?

  3. Why Do We Need the Singleton Pattern?

  4. Problem Statement

  5. Real-World Analogy

  6. Key Characteristics

  7. UML Class Diagram

  8. Basic Singleton Implementation

  9. Thread-Safe Singleton

  10. Lazy Singleton

  11. Singleton in ASP.NET Core

  12. Real-World Use Cases

  13. Advantages

  14. Disadvantages

  15. Best Practices

  16. Common Mistakes

  17. Singleton vs Static Class

  18. Interview Questions

  19. Summary


Introduction

As applications become more complex, there are situations where creating multiple instances of a class is unnecessary or even harmful. Consider services such as logging, application configuration, caching, or database connection management. If every component creates its own instance, the application may waste memory, produce inconsistent state, or become difficult to manage.

The Singleton Design Pattern solves this problem by ensuring that only one instance of a class exists throughout the application's lifetime while providing a single, globally accessible point to that instance.

The Singleton Pattern is one of the Creational Design Patterns introduced by the Gang of Four (GoF) and is widely used in enterprise applications, including those built with C# and ASP.NET Core.


What is the Singleton Pattern?

Definition

The Singleton Pattern ensures that:

  • Only one instance of a class can exist.

  • A global access point is provided to that instance.

  • The instance is created in a controlled manner.

In other words, regardless of how many times your application requests the object, it always receives the same instance.


Why Do We Need the Singleton Pattern?

Imagine a logging service used throughout an application.

Without Singleton:

  • Every class creates its own logger.

  • Multiple log files or streams may be opened.

  • Memory usage increases.

  • Configuration may become inconsistent.

With Singleton:

  • One shared logger handles all logging requests.

  • Consistent behavior is maintained.

  • Resource usage is minimized.


Problem Statement

Suppose you have a configuration manager.

public class ConfigurationManager
{
}

Now imagine different parts of the application create their own instances:

var config1 = new ConfigurationManager();
var config2 = new ConfigurationManager();
var config3 = new ConfigurationManager();

This creates three independent objects, which may each load the same configuration and consume additional resources unnecessarily.

The Singleton Pattern ensures there is only one shared instance.


Real-World Analogy

Imagine an office with one printer.

Hundreds of employees can send print jobs, but they all use the same physical printer.

The printer represents the Singleton instance:

  • One printer

  • Many users

  • Shared access

Another example is the President of a company. There is only one CEO at a time, and everyone interacts with that same person rather than creating new CEOs.


Key Characteristics

  • Only one object exists.

  • Constructor is private.

  • Object creation is controlled internally.

  • Global access point is provided.

  • Prevents accidental instantiation.


UML Class Diagram

                +----------------------+
                |     Singleton        |
                +----------------------+
                | - instance           |
                +----------------------+
                | - Singleton()        |
                | + Instance           |
                | + DoWork()           |
                +----------------------+

Explanation:

  • The constructor is private.

  • The static Instance property returns the single object.

  • Clients access the object through Instance.


Basic Singleton Implementation

public sealed class Logger
{
    private static Logger _instance;

    private Logger()
    {
    }

    public static Logger Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new Logger();
            }

            return _instance;
        }
    }

    public void Log(string message)
    {
        Console.WriteLine($"[LOG] {message}");
    }
}

Usage

class Program
{
    static void Main()
    {
        Logger logger1 = Logger.Instance;
        Logger logger2 = Logger.Instance;

        logger1.Log("Application Started");

        Console.WriteLine(object.ReferenceEquals(logger1, logger2));
    }
}

Output

[LOG] Application Started
True

True confirms that both variables reference the same object.


Why Use sealed?

The sealed keyword prevents other classes from inheriting the Singleton class.

public sealed class Logger
{
}

This avoids scenarios where inheritance could accidentally allow multiple instances or alter the Singleton's intended behavior.


Thread-Safe Singleton

The basic implementation is not thread-safe. If two threads access Instance simultaneously, they could each create a new object.

A common solution is to use a lock.

public sealed class Logger
{
    private static Logger _instance;
    private static readonly object _lock = new object();

    private Logger()
    {
    }

    public static Logger Instance
    {
        get
        {
            lock (_lock)
            {
                if (_instance == null)
                {
                    _instance = new Logger();
                }

                return _instance;
            }
        }
    }
}

This guarantees that only one thread creates the instance.


Lazy Singleton

The .NET Framework provides the Lazy<T> class, which simplifies thread-safe lazy initialization.

public sealed class Logger
{
    private static readonly Lazy<Logger> _instance =
        new Lazy<Logger>(() => new Logger());

    private Logger()
    {
    }

    public static Logger Instance => _instance.Value;

    public void Log(string message)
    {
        Console.WriteLine($"[LOG] {message}");
    }
}

Why prefer Lazy<T>?

  • Thread-safe by default

  • Cleaner implementation

  • Object created only when first accessed

  • Recommended for most new applications


Singleton in ASP.NET Core

ASP.NET Core has built-in support for singleton services through Dependency Injection.

Service

public interface ILoggerService
{
    void Log(string message);
}

public class LoggerService : ILoggerService
{
    public void Log(string message)
    {
        Console.WriteLine(message);
    }
}

Register in Program.cs

builder.Services.AddSingleton<ILoggerService, LoggerService>();

Inject into a Controller

public class HomeController : Controller
{
    private readonly ILoggerService _logger;

    public HomeController(ILoggerService logger)
    {
        _logger = logger;
    }

    public IActionResult Index()
    {
        _logger.Log("Home page requested.");
        return View();
    }
}

The Dependency Injection container creates one instance of LoggerService and shares it across the application's lifetime.

Note: In ASP.NET Core, prefer using the built-in Dependency Injection container (AddSingleton) instead of implementing your own Singleton class unless you have a specific reason.


Real-World Use Cases

The Singleton Pattern is suitable for services that represent shared application-wide resources.

Use CaseWhy Singleton Fits
Logging ServiceA single logging pipeline simplifies management.
Application ConfigurationOne shared configuration source avoids duplication.
In-Memory CacheCentralized cache improves consistency.
License ManagerOne authority validates application licensing.
Printer SpoolerCoordinates access to a shared printer.
Feature Flag ServiceMaintains a consistent set of feature toggles.

Advantages

  • Ensures a single shared instance.

  • Reduces unnecessary object creation.

  • Provides a centralized access point.

  • Conserves memory and resources.

  • Simplifies management of shared services.

  • Works well with Dependency Injection.


Disadvantages

  • Introduces global state, which can make reasoning about the application harder.

  • Can complicate unit testing if overused.

  • May hide dependencies if accessed directly instead of through Dependency Injection.

  • Incorrect implementations may not be thread-safe.

  • Overuse can lead to tightly coupled code.


Best Practices

  • Prefer Lazy<T> for manual Singleton implementations.

  • Use sealed to prevent inheritance.

  • Keep Singleton classes focused on one responsibility.

  • Avoid storing mutable global state unless necessary.

  • In ASP.NET Core, use AddSingleton through the DI container whenever possible.

  • Inject Singleton services into consumers rather than calling static Instance properties throughout the codebase.


Common Mistakes

Public Constructor

public Logger()
{
}

This allows anyone to create additional instances.

Correct approach: Make the constructor private.


Ignoring Thread Safety

In multi-threaded applications, a non-thread-safe Singleton can produce multiple instances.

Correct approach: Use Lazy<T> or proper synchronization.


Using Singleton Everywhere

Not every service should be a Singleton.

For example, classes that maintain request-specific or user-specific state are usually better suited to scoped or transient lifetimes in ASP.NET Core.


Singleton vs Static Class

FeatureSingletonStatic Class
InstanceOne objectNo object
Implements Interfaces✔ Yes✖ No
Dependency Injection✔ Yes✖ No
Inheritance✔ Can implement interfaces (though the class itself is often sealed)✖ Not supported
Can Hold State✔ Yes✔ Yes (static state)
Lazy Initialization✔ YesLimited (type initialization semantics)
Supports Polymorphism✔ Yes✖ No

When to choose which?

  • Use a Singleton when you need an object with identity that participates in Dependency Injection and can implement interfaces.

  • Use a static class for stateless utility methods, such as mathematical helpers or string manipulation functions.


Frequently Asked Interview Questions

1. What is the Singleton Design Pattern?

It is a creational design pattern that ensures only one instance of a class exists and provides a global access point to that instance.


2. Why is the constructor private?

A private constructor prevents external code from creating additional instances using the new keyword.


3. Why should a Singleton class often be sealed?

To prevent inheritance from changing or bypassing the Singleton behavior.


4. Is the basic Singleton implementation thread-safe?

No. Concurrent access can result in multiple instances being created.


5. What is the recommended Singleton implementation in modern C#?

Using Lazy<T> for manual implementations, or leveraging the ASP.NET Core Dependency Injection container with AddSingleton.


6. Can Singleton work with Dependency Injection?

Yes. In fact, ASP.NET Core encourages registering shared services with AddSingleton instead of implementing the pattern manually in many cases.


7. What are some real-world examples of Singleton?

  • Logging services

  • Configuration providers

  • In-memory caches

  • Printer spoolers

  • Feature flag managers


Summary

The Singleton Design Pattern is one of the simplest yet most widely used creational patterns. It ensures that only one instance of a class exists and provides controlled global access to that instance. While manual implementations are useful for understanding the concept, modern ASP.NET Core applications typically rely on the built-in Dependency Injection container to manage singleton lifetimes.

Used appropriately, Singleton can improve resource management and maintain consistent application-wide behavior. However, it should be applied thoughtfully, avoiding unnecessary global state and ensuring thread safety where required.


What's Next?

In Part 2.2, we'll explore the Factory Method Design Pattern, where you'll learn:

  • Why direct object creation with new can become a maintenance problem

  • How the Factory Method Pattern decouples object creation from object usage

  • UML Class Diagram

  • Complete C# examples

  • ASP.NET Core implementation

  • Real-world scenarios

  • Advantages and disadvantages

  • Factory Method vs. Simple Factory

  • Common interview questions

This will build on the Singleton concepts and further strengthen your understanding of creational design patterns in modern .NET development.

Factory Design Pattern Part 2.2

Introduction to Design Patterns


Don't Copy

Protected by Copyscape Online Plagiarism Checker