What is the Singleton Design Pattern?
- Manage shared resources (like logging, configuration, cache, or thread pool).
- Avoid duplicate object creation for performance reasons.
- Maintain a single source of truth.
- 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#?
Approach 1: Non-Thread Safe Implementation.
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.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}"); } }
//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");
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
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.
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; } }
Thread t1 = new Thread(() => { var s1 = Singleton.GetInstance(); }); Thread t2 = new Thread(() => { var s2 = Singleton.GetInstance(); }); t1.Start(); t2.Start(); t1.Join(); t2.Join();
Instance Created.
Approach 3: Singleton Using .NET Lazy<T>
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); } }
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) }"); } }
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
When to Use Singleton Pattern
- 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
- 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).

