The Singleton Pattern in C#: Thread-Safe Implementation
Typically the first design pattern most developers learn and use, sometimes wrongly ☺ To give an introduction, we can say that singleton is one of the creational design patterns which ensures only one class instance with single point of access through entire application.
Because it is relatively simple to implement, it is sometimes misapplied in situations where it is not the most suitable choice.
When To Use It?
It should be used as the definition says, when we need single point of access and only one instance during the application runtime. Here are the few examples:
-
Configuration Management Centralized configuration settings for consistent use through the application. Configuration is by nature static and have constant value(s) during the runtime.
-
Caching Maintaining Single instance of cached objects for easy and fast access. Cached values can be changed during the runtime, but we need single global point to access it and every part of application needs to have access to the shared object.
-
Logging Ensure unified mechanism to avoid duplication of log files, formats, etc.
-
Global State Management Centralized management of the state which is needed to be shared across the application.
-
Resource sharing Thread pools, database connection, I/O operations.
When Not To Use It?
On the other hand, here are few examples on when not to use it:
-
When you have multiple instances of the application by infrastructure design. Here you need to be careful because in most cases it won’t behave as expected, because every process will have its own instance.
-
When you can solve problem easy without overcomplicating it Strive to keep your design as simple as possible and avoid unnecessary complexity.
-
When by design you need stateless service. Here singleton does more harm than help because it has, by default, some state which is intended to be shared.
-
When application needs to overwrite initialization parameters frequently. This is clear sign to not use it in this place.
-
High concurrency systems. Here, singleton instance can become bottleneck and actually be misused in order to keep it synced all the time.
Here I would also add one case when it should be careful with singleton usage. It is, by default, globally accessible and this potentially hides the dependency on it. If some class is using it, its dependency will be hidden. This is problematic when you try to test some class, where you are unable to isolate and mock external dependencies, because singleton is already inside the class without being injected in standard way.
Code Example
Here is a thread safe C# implementation of the pattern:
public sealed class Singleton
{
private static Singleton _instance;
private static readonly object _lock = new object();
private Singleton()
{
// initialization code
}
public static Singleton Instance
{
get
{
if (_instance == null)
{
lock (_lock)
{
if (_instance == null)
{
_instance = new Singleton();
}
}
}
return _instance;
}
}
public void Hello()
{
Console.WriteLine("Hello from singleton.");
}
}
class Program
{
static void Main(string[] args)
{
Singleton.Instance.Hello();
}
}
Part 1 of 8 in the "Design Patterns" series
- The Singleton Pattern in C#: Thread-Safe Implementation
- The Builder Pattern in C#: Constructing Complex Objects Cleanly
- Implementing the Factory Method Pattern Elegantly in C#
- The Strategy Pattern in C#: Real-World Use Cases
- Architecting Testable WinForms Applications Using the MVP Pattern
- How to Use the Decorator Pattern in C# to Extend Code Safely
- Understanding the Prototype Pattern and Deep Cloning in C#
- The Adapter Pattern in C#: Bridging Incompatible Interfaces