Singleton is one of the first and simplest software design patterns that you may encounter as a developer. However, it is often also considered an anti-pattern.
In this article, we will take a closer look at the Singleton Design Pattern in C# and learn how to implement it properly. We will explore some common use cases, and discuss why it is sometimes better to avoid it altogether. We will conclude with an alternative pattern you can use in most cases.
This article is published from the DNC Magazine for Developers and Architects. Download this magazine from here [PDF] or Subscribe to this magazine for FREE and download all previous and current editions.
The Singleton Pattern in C#
The Singleton pattern was originally introduced in the famous Gang of Four book (i.e. Design Patterns: Elements of Reusable Object-Oriented Software by Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides). The authors described the intent of the pattern as follows:
Ensure a class only has one instance, and provide a global point of access to it.
To prevent multiple instances of the class, we need to make the constructor private. The class will take care of instantiation itself and provide access to the created instance via a static property. This is the simplest possible implementation in C#:
public sealed class Singleton
{
private static readonly Singleton _instance = new Singleton();
public static Singleton Instance
{
get
{
return _instance;
}
}
private Singleton()
{
// place for instance initialization code
}
}
The class instance is created during static type initialization, which happens when accessing any (static or instance) type member for the first time. If the type has additional members, this initialization could happen before we actually want to use the singleton instance. For types with expensive initialization, we might want to delay the instantiation until we really want to access it. The easiest way to achieve this is by using Lazy:
public sealed class Singleton
{
private static readonly Lazy<Singleton> _lazyInstance =
new Lazy<Singleton>(() => new Singleton());
public static Singleton Instance
{
get
{
return _lazyInstance.Value;
}
}
private Singleton()
{
// place for instance initialization code
}
}
Lazy is a helper class introduced in .NET Framework 4 which performs the initialization when its Value property is accessed for the first time, and ensures that the initialization will happen only once even in multi-threaded scenarios.
Singleton Pattern - Typical Use Cases
Singleton’s design makes it very useful for accessing unique external resources, which require additional management. A typical example of such a resource is the application log: there is usually only one and it requires additional operations such as opening, closing, flushing, splitting into multiple files based on size or time, etc. A common approach to representing this resource in objected-oriented programming (OOP) paradigm is to implement a service with a simple interface, which transparently takes care of all the resource technicalities.
A singleton is a convenient way for accessing the service from anywhere in the application code.
The model quickly falls apart when the service not only provides access to operations but also encapsulates state, which affects how other code behaves. Application configuration is a good example of this. In the best case, the configuration is read once at the application start and does not change for the entire lifetime of the application.
However, different configuration can cause a method to return different results although no visible dependencies have changed, i.e. the constructor and the method have been called with the same parameters. This can become an even bigger problem if the singleton state can change at runtime, either by rereading the configuration file or by programmatic manipulation. Such code can quickly become very difficult to reason with:
var before = new MyClass().CalculateResult(3, 2);// depends on Configuration.Instance
RefreshConfiguration(); // modifies values in Configuration.Instance
var after = new MyClass().CalculateResult(3, 2); // depends on Configuration.Instance
Without comments, an uninformed reader of the code above could not expect the values of before and after to be different, and could only explain it after looking into the implementation of the individual methods, which read and modify global state hidden in Configuration singleton.
Let us look at the same code, this time explicitly stating its dependency on the Configuration class, which does not need to be a singleton any more:
var configuration = new Configuration();
var before = new MyClass(configuration).CalculateResult(3, 2);
RefreshConfiguration(configuration);
var after = new MyClass(configuration).CalculateResult(3, 2);
If values of before and after were different this time, one would immediately investigate the configuration that is being passed to all the methods.
Unit Testing Singleton
There are even more disadvantages to singletons when writing unit tests.
Creating a dependency in the test and setting it up correctly before passing it to the method under test, is much more self-explanatory, than changing some value on a specific singleton:
// explicit dependency
var configuration = new Configuration();
configuration.Mode = Mode.Basic;
var result = new MyClass(configuration).CalculateResult(3, 2);
// implicit dependency
Configuration.Instance.Mode = Mode.Basic;
var result = new MyClass().CalculateResult(3, 2);
Additionally, the state of a singleton is actually a global state. Modifying it in one test will affect all other tests that run after it, unless they explicitly set the value back according to their requirements. This will make test results unreliable: a test will pass when it is run on its own, and will fail when it runs together with the other tests. Troubleshooting this can be difficult and time consuming.
Having no global state avoids such issues altogether.
However, even if the singleton has no state, it can still make testing more difficult in some aspects.
With a singleton application log, the test code will still invoke the same logger implementation as production code. Unless we globally disable logging for tests (if that is supported), this will unnecessarily slow down the execution of tests and fill up the log files.
It will also make it almost impossible to test the logging in the method under test. Any logging calls will always end up in the real logger implementation, which probably is not and should not be designed in a way that would make it easy for tests to inspect it.
If there was a way to replace the default logger in tests (e.g. by passing it explicitly to the class or method under test), we could have a different implementation for testing that would allow direct checking of logged messages.
Multi-threaded Applications
Another challenge for singletons are multi-threaded applications. Since there is only one class instance available to all threads, all the methods need to be thread-safe, i.e. they must work correctly even when called in parallel from multiple threads. This is a non-trivial requirement for a logger that logs the messages into a log file.
I/O operations are not thread-safe and writing to a file needs to be synchronized (making the logging method slower) or queued (increasing the time between the method call and actual writing to the file).
Passing the logger as an explicit dependency instead of making it available as a singleton would allow each thread to use its own logger instance. This would avoid the above-mentioned issues with thread safety at the cost of having multiple log files – one for each thread.
Depending on the requirements, this could be a better solution for the problem. If not, the same logger instance could still be passed to all threads.
Dependency Injection
Throughout the article, I have been advocating explicit passing of dependencies to classes in favor of implicit methods. This approach is called dependency injection. As the name suggests, the idea is to pass all dependencies as constructor parameters when instantiating the class, instead of creating new instances of dependencies inside the class or directly referencing global instances, such as singletons.
As we already learned, this gives full control over the dependencies to the caller. All the dependencies can be defined in a single location, i.e. the application entry point. This location is typically called the composition root. The actual location in the application code depends on the type of the application:
- In a console application, this is its startup code, i.e. the Main method.
- In web applications, this is the code handling the client requests, e.g. the controller factory in ASP.NET MVC.
- In desktop applications, this is the code responsible for setting up new views, e.g. view model locator in many WPF MVVM frameworks.
- In tests, this is each test method.
As you can see, whatever the runtime is, composition root is always the location, where new components are constructed to handle incoming requests. To learn more about composition roots in C# and dependency injection in general, check the series of articles (bit.ly/dnc-yacoub) in DNC Magazine, written by Yacoub Massad.
The composition root is not only responsible for creating the main application components and providing dependencies to them, but also for controlling the lifetime of individual dependencies.
Sometimes a new instance of a dependency will be passed to each component; at other times a dependency will only have a single instance for the entire lifetime of the application that is shared between all of the components, as is the case with singletons.
While you could handle all of that in your own code, you can usually make your job easier by using a dedicated dependency injection library, i.e. an IoC (inversion of control) container as they are often called:
// create the container
var container = new Container();
// configure scope
container.Options.DefaultScopedLifestyle = new WebRequestLifestyle();
// register dependencies
container.Register<ILogger, FileLogger>(Lifestyle.Singleton);
container.Register<IRepository, DbRepository>(Lifestyle.Scoped);
container.Register<HomeController>(Lifestyle.Transient);
// request composed instance
var controller = container.GetInstance<HomeController>();
The above snippet uses Simple Injector, one of the most popular dependency injection libraries for .NET.
It demonstrates, how dependencies can be registered as implementations of interfaces (FileLogger and DbRepository) or as standalone class implementations (HomeController).
It also sets up different lifetimes for dependencies: singleton (one instance for the duration of the application), scoped (one instance per scope, which can be configured separately – it is set to web request in this sample) or transient (new instance each time, even if the same dependency is required multiple times in a single object graph). By calling GetInstance, the container instantiates the requested class and provides it with required dependencies, e.g. HomeController could have ILogger and IRepository as its dependencies.
Modern application frameworks often have a basic dependency injection framework already built-in and provide the option of replacing it with another one for more advanced scenarios. ASP.NET Core is an example of such a framework. Using its built-in dependency injection, we can easily configure our own dependencies in ConfigureServices method of Startup class:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddSingleton<ILogger, FileLogger>();
services.AddScoped<IRepository, DbRepository>();
}
Now we can add a constructor requiring these dependencies and the framework will automatically satisfy them, observing the configured lifetime:
public HomeController(ILogger logger, IRepository repository)
{
_logger = logger;
_repository = repository;
}
This allows us to define our FileLogger as a singleton in only a few lines of code, without the disadvantages brought by implementing the classic singleton pattern.
Conclusion
Singleton is one of the basic software design patterns from the Gang of four book. It is an appropriate choice for providing managed access to unique external resources, but can quickly be abused by storing global state in it. Even without that, it can introduce difficulties when writing unit tests or developing multi/threaded applications.
As an alternative to singletons, we can pass the instance as an explicit dependency to class constructors that need it. To make this process easier, we can use a dependency injection library. Some application frameworks already have such a library built into them which can provide a complete replacement for the singleton pattern.
This article has been editorially reviewed by Suprotim Agarwal.
C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.
We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).
Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.
Click here to Explore the Table of Contents or Download Sample Chapters!
Was this article worth reading? Share it with fellow developers too. Thanks!
Damir Arh has many years of experience with software development and maintenance; from complex enterprise software projects to modern consumer-oriented mobile applications. Although he has worked with a wide spectrum of different languages, his favorite language remains C#. In his drive towards better development processes, he is a proponent of Test-driven development, Continuous Integration, and Continuous Deployment. He shares his knowledge by speaking at local user groups and conferences, blogging, and writing articles. He is an awarded Microsoft MVP for .NET since 2012.