Singleton Pattern
A story of redemption
Once upon a time, the Singleton pattern was the arch enemy of unit testing. Many may still call it an anti-pattern, but if that is true; why is it a first-class type of object lifetime in Microsoft Dependency Injection (and every other DI container)? Let’s look at what a Singleton’s history, what it is, how we can use it for good and how it can easily be misused and give a developer headache.
No really, what is it?
As the name implies, a Singleton is a single instance of an object. And by “single instance” I don’t simply mean, there just happens to be one; rather only allow one is allowed for the life of the application. The utility of the Singleton is it can be a single source of global state. This is useful for thread and connection management as well as caching. Essentially if you want a single place to look in code, a Singleton is a good choice.
The reason we need an instance instead of a simple static class has to do with dependencies. Static classes can’t depend on an instance of
ISomething.
Now let’s look at that example:
namespace DotnetDev.Singleton;
public class MySingletonClass
{
private static readonly object Lock = new ();
private static MySingletonClass _instance;
private MySingletonClass()
{}
public void DoSomething()
{
Console.WriteLine("Doing stuff...");
}
public static MySingletonClass Instance()
{
if (_instance != null) return _instance;
lock (Lock)
{
_instance ??= new MySingletonClass();
}
return _instance;
}
}It might look like a lot, but let’s break it down:
The
ctoris private so that no code cannewup a new instance.To get the instance, calling code calls a static method called
Instance(). This would make it hard to use with a DI container.There is a double-check lock in place to force threads to line up in single-file to go through the
Instance()method (thus preventing multiple threads from creating more than one).Once the instance is obtained, calling code can
DoSomething(). We could add more methods if we wanted.
This was a very common pattern before we used DI containers. The biggest misuse of this style of Singleton is the fact that any code, anywhere in your app; can pluck MySingletonClass out of the ether and get an instance through that static accessor. All you had to do to get a reference to the Singleton is to insert this line of code anywhere in your app:
MySingletonClass.Instance().DoSomething();This feature is the probably the biggest reason Singleton’s get a bad reputation. Any dependencies living in the Singleton are now inside the calling class making side-effects unpredictable in unit testing. On top of that, mocking a Singleton built like this becomes very difficult. The Singleton was very much at risk of being relegated to the annals of history, it needed a savior.
And then came DI containers…
Born Again
Older dev’s will no doubt remember quite a bit of back-and-forth on the value vs risk of having Singletons in your app. But what if disallowed the worst part of the Singleton? If we could still access the Singleton, but in a unit test friendly way; could we redeem the value of this pattern?
It turns out we can, in fact we can write our new and improved Singleton in a way that removes all the boilerplate code needed in the previous example. Let’s take a look at the new way to write it:
namespace DotnetDev.Singleton;
public class MyNewSingleton
{
public void DoSomething()
{
Console.WriteLine("Doing stuff...");
}
}You’ll notice the following:
There’s no thread locking needed.
There’s no explicit private
ctorbut there is an implied public one.There’s no static accessor method (which was the main trouble).
At this point you should be asking yourself, how does this code restrict instances to one? It doesn’t yet, but we will handle that soon. That’s because in modern Dotnet apps, we should be using the DI container to handle instantiating the classes we use. Let’s register our new class as a Singleton with the container:
var services = new ServiceCollection();
services.AddSingleton<MyNewSingleton>();At this point we can resolve it right from the container (after we build it) or via constructor injection as we most often use. Here’s code that resolves the Singleton, calls a method then resolves another one. Because we registered our class as a Singleton, both instances should be equal:
var container = services.BuildServiceProvider();
var myProperSingleton = container.GetRequiredService<MyNewSingleton>();
myProperSingleton.DoSomething();
var anotherOne = container.GetRequiredService<MyNewSingleton>();
Console.WriteLine(myProperSingleton == anotherOne); //trueSo what did we learn?
Singleton’s that have static accessor methods are historically bad news. Using Singleton’s with modern DI containers are not only good, they are encouraged when you need global state. Make sure you access the Singleton through constructor injection. The only tradeoff we had to make was to make the constructor public on MyNewSingletonClass. This means you have to be on the lookout for any dev’s manually instantiating an instance. Hopefully most of your team already have good IoC hygiene.
Remember, by design there will only be a single instance of your Singleton for the life of the app.
Here’s a full working example for you to look at, happy coding!

