C# Thread-Safe Singleton Pattern Using Lazy
This article provides a concise guide to implementing the Singleton
design pattern in C# using the System.Lazy<T> type.
You will learn how Lazy<T> simplifies lazy
initialization, provides inherent thread safety without explicit lock
statements, and reduces boilerplate code while adhering to best software
design practices.
The Implementation
The most robust and idiomatic way to implement a thread-safe
singleton in modern C# (.NET 4.0 and later) is by leveraging
System.Lazy<T>. Here is the complete
implementation:
public sealed class Singleton
{
// Lazy<T> handles thread-safe, deferred instantiation automatically
private static readonly Lazy<Singleton> _instance =
new Lazy<Singleton>(() => new Singleton());
// Public accessor to get the singleton instance
public static Singleton Instance => _instance.Value;
// Private constructor prevents direct instantiation from outside
private Singleton()
{
// Initialization code here
}
public void DoWork()
{
// Instance method logic
}
}Key Components Explained
sealedClass Modifier: Marking the class assealedprevents other classes from inheriting from it. Inheritance can introduce multiple instances or violate the singleton constraint through derived constructors.Private Constructor: The parameterless constructor is marked
privateto ensure that the class cannot be instantiated directly with thenewkeyword from external code.static readonly Lazy<Singleton>: TheLazy<T>wrapper encapsulates the instance creation logic. By default,Lazy<T>is thread-safe (LazyThreadSafetyMode.ExecutionAndPublication). This ensures that only one thread can execute the factory delegate, even if multiple threads attempt to access the instance simultaneously.InstanceProperty: The public staticInstanceproperty exposes the singleton to consumers. Accessing the.Valueproperty triggers the factory method if the instance has not yet been initialized; otherwise, it immediately returns the cached instance.
Advantages of Using
Lazy<T>
- Implicit Thread Safety: You avoid manual
double-check locking patterns (
lock,volatile), which are error-prone and harder to read. - True Lazy Loading: The instance is created strictly
on the first call to
Singleton.Instance, avoiding premature initialization during static class loading. - Performance:
Lazy<T>uses highly optimized internal synchronization primitives, minimizing overhead once the object has been initialized.