Slider

Singleton Design Pattern n C#.

The Singleton Design Pattern is one of the simplest yet most important creational design patterns in software engineering. It ensures that only one instance of a class exists in memory throughout the application's lifetime and provides a global point of access to that instance.

Design Pattern

What is the Singleton Design Pattern?

A Singleton class allows only one object of itself to be created and shared across the entire application. This pattern is extremely useful when you need to:

  • Manage shared resources (like logging, configuration, cache, or thread pool).
  • Avoid duplicate object creation for performance reasons.
  • Maintain a single source of truth.
Think of a printer spooler or Windows Task Manager. You can open it multiple times, but under the hood, only one instance manages all print jobs or processes. That’s a Singleton in action, one object serving the whole system.

Does who don't know: The printer spooler service is a Windows service that manages and queues print jobs for your printer.

Key Characteristics 
  • Only one instance of the class is created. 
  • Global access to that instance through a public static property or method. 
  • Thread-safety is ensured in multi-threaded environments. 
  • Lazy initialization means the object is created only when needed.

How To Implement the Singleton Design Pattern in C#?

There are multiple ways to implement the Singleton design pattern in C#, and here we are going to discuss all, and at the end will decide which one is best and easiest to implement.

Approach 1: Non-Thread Safe Implementation.

This simple Singleton creates only one instance of the Singleton class. It uses a private static field _instance to store that single object. The constructor is private, so no one can create the object using new. The GetInstance() method checks if it _instance is null — if yes, creates the object; if not, returns the existing one.

Example Code:
public class Singleton
{
    private static Singleton? _instance;

    private Singleton() { }

    public static Singleton GetInstance()
    {
        if (_instance == null)
            _instance = new Singleton();
        return _instance;
    }

    public void Message(string message)
    {
        Console.WriteLine($"{DateTime.Now}: {message}");
    }
}
Using a Singleton Class in a Program.cs
//Singleton Design
var instance1 = Singleton.GetInstance();
var instance2 = Singleton.GetInstance();

if(instance1 == instance2) Console.WriteLine("True- Both refer to same instance.");

instance1.Message("Log from Instance 1");
instance2.Message("Log from Instance 2");
Output:
True- Both refer to same instance.
28-11-2025 16:39:40: Log from Instance 1
28-11-2025 16:39:40: Log from Instance 2

This version isn’t thread-safe. If two threads call GetInstance() Simultaneously, two objects could be created. Let's understand another approach to make our code thread-safe safe so only one instance is possible because that is our goal in a singleton.

Approach 2: Thread-Safe Singleton.

A Thread-Safe Singleton ensures that only one instance of a class is created, even when multiple threads try to create it at the same time. It prevents race conditions and guarantees that all threads receive the same shared instance safely.

Example Code:
public class Singleton
{
    private static Singleton? _instance;
    private static readonly object _lock = new object();

    //private constructor to prevents extension instantiation
    private Singleton()
    {
        Console.WriteLine("Instance Created.");
    }

    public static Singleton GetInstance()
    {
        //1st check without lock
        if(_instance == null)
        {
            lock (_lock) //only one thread can enter at a time
            {
                //2nd check with lock
                if(_instance == null)
                {
                    _instance = new Singleton();
                }
            }
        }
        return _instance;
    }
}

When using a non-thread-safe Singleton, two threads can reach the line if(_instance == null) at the same time. Both threads see that the instance is null and both try to create the object. In double-checked locking, Thread A enters the lock first and creates the Singleton instance, while Thread B waits outside the lock.

Once Thread A finishes creating the instance and leaves the lock, Thread B enters the lock. If we didn’t check _instance again inside the lock, Thread B would also create a new instance, overwriting the first one.

Multiple Threads Calling It:
Thread t1 = new Thread(() =>
{
    var s1 = Singleton.GetInstance();
});

Thread t2 = new Thread(() =>
{
    var s2 = Singleton.GetInstance();
});

t1.Start();
t2.Start();

t1.Join();
t2.Join();
Output:
Instance Created.

Even with 2 (or 100) threads, the constructor runs only once.

The above approach solves my multiple-thread problem, but it is a lot of code to write, and handling threads manually is not an easy job. So, .NET provides a better solution to implement Singleton more easily and effectively. Let's discuss that now.

Approach 3: Singleton Using .NET Lazy<T>

Lazy<T> is a special .NET class that automatically handles lazy initialization + thread safety for you. It ensures the Singleton object is created only when accessed, and only once, even if multiple threads call it at the same time.

Does Who Don't KnowLazy initialization is a technique used to delay the creation of an object until it is actually needed. This can improve performance and reduce memory usage, especially for objects that are expensive to create or may not be used at all during the program's execution.

Example Code:
public sealed class Singleton
{
    // Lazy ensures thread-safe, lazy initialization
    private static readonly Lazy<Singleton> _instance =
        new Lazy<Singleton>(() => new Singleton());

    // Private constructor prevents external creation
    private Singleton()
    {
        Console.WriteLine("Singleton Instance Created");
    }

    // Public accessor to get the single instance
    public static Singleton Instance => _instance.Value;

    public void ShowMessage(string msg)
    {
        Console.WriteLine("Message: " + msg);
    }
}

Calling inside Program.cs
class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Accessing Singleton First Time:");
        Singleton s1 = Singleton.Instance;
        s1.ShowMessage("Hello from First Instance");

        Console.WriteLine("\nAccessing Singleton Second Time:");
        Singleton s2 = Singleton.Instance;
        s2.ShowMessage("Hello from Second Instance");

        Console.WriteLine($"\nAre both instances same?  { (s1 == s2) }");
    }
}
Output:
Accessing Singleton First Time:
Singleton Instance Created
Message: Hello from First Instance

Accessing Singleton Second Time:
Message: Hello from Second Instance

Are both instances same?  True

This approach is fully thread-safe with cleaner code and no manual lock required.

When to Use Singleton Pattern

Use Singleton when:
  • You need exactly one instance for coordination (e.g., logging service, configuration manager).
  • Creating multiple instances would cause conflicts or inconsistency.
  • You need shared state or cache across different parts of the app.

When Not to Use Singleton

Avoid Singleton when:
  • Your class maintains mutable state - it can cause unexpected side effects.
  • It leads to tight coupling - other classes depend directly on the singleton.
  • It complicates unit testing (since global state can persist across tests).

The Singleton Design Pattern is powerful and useful but like all global patterns, it must be used wisely. Overusing it can lead to tight coupling and hidden dependencies. However, when applied correctly for shared resources or configuration objects it provides an elegant, thread-safe way to ensure a single source of truth across your application.
0

No comments

Post a Comment

both, mystorymag

DON'T MISS

AI
© all rights reserved
made with by WorkWithG
Table of Contents