Singleton Pattern
The Singleton Pattern is probably the most well-known design patterns that came out of Gang of Four’s book Design Patterns: Elements of Reusable Object-Oriented Software. This pattern is one of the creational patterns, and basically, it ensures that only one object of a particular class is created and shared among other members of the system. Singleton class should enable easy access to its only instance to the other classes.
But this design pattern carries a lot of controversies too. Some consider it an antipattern and cause of code smell. Among many complaints, people often argue that it violate Single Responsibility Principle, due to the fact that it handles its own creation, that it couples code and that it is hard to test. And even thought some of these issues were addressed by Uncle Bob himself, there are a lot of good points that Singleton class shouldn’t be used. Heck, there is a good chance that people will hate you just by writing one. But…
Singleton patterns still solve one problem that you may encounter – Resource Contention. This is a situation where you need one instance of the object and you need to manage that instance. There are not many cases of this situation and one example of this is a single log file. There are suggestions that you can solve this problem with Dependancy Injection which you should also consider. But, in case you need Singleton let’s take a look at some good and bad ways to implement Singleton class.
Standard implementation
Singleton pattern is also one of the most commonly asked interview questions. Usually, candidates are asked to write one version of singleton implementation. If you google this you’ll probably find few hits that will suggest an implementation that looks like this:
namespace SingletonExamples { // Broken, non thread-save solution. // Don't use this code. public class SingletonFirst { private static SingletonFirst _instance; private SingletonFirst() { } public static SingletonFirst Instance { get { if (_instance == null) { _instance = new SingletonFirst(); } return _instance; } } } }
Now this will probably pass as a fine answer on an interview, but this code definitely has a lot of problems, apart from not being very impressive one. The biggest problem is that this code isn’t thread-safe, and that is why you shouldn’t use this code. Yes, now you are using threads and Singleton and everyone will avoid you in the office. Nevertheless, let’s go trough this thought exercise. We don’t want to end up in the situation where two threads evaluate if (_instance == null) as true and each creates an instance of the class.
Thread-safe implementation
So, let’s do something like this:
namespace SingletonExamples { public class SingletonThreadSafe { private static SingletonThreadSafe _instance; private static readonly object _lock = new object(); private SingletonThreadSafe() { } public static SingletonThreadSafe Instance { get { lock (_lock) { if (_instance == null) { _instance = new SingletonThreadSafe(); } return _instance; } } } } }
There it is, we used locks to make our singleton class thread-safe. But, locks are not very good synchronization mechanisms in this case. Because the lock is required everytime Instance is asked for, the performance of this code will be degraded. This problem can be avoided if we ask if the instance is null before locking and then asking is instance null once again after acquiring the lock. Now, that would look ugly. Also, we already have one if-null combo, and we can agree that controlling flow with if-null combos is not such a good idea and that it indicated that we have problems in our design. Can we try to use the static constructor?
Implementation with static constructor
Static constructors will assure that it has been run only once per application domain, meaning that our Singleton class can look like this:
namespace SingletonExamples { public class SingletonStatic { private static SingletonStatic instance; private SingletonStatic() { } static SingletonStatic() { instance = new SingletonStatic(); } public static SingletonStatic Instance { get { return instance; } } } }
The first thread that wants to get property Instance will trigger static constructor, also known as type initializer. Other threads that try to read the Instance property will be locked until static constructor has finished. Only once it complete its task, other threads will be allowed to get Instance value. So, locking on every time someone tries to get an instance is avoided.
But…people will have a lot of comments on that static constructor of yours. Usually something along the line that they could cause deadlocks if you perform blocking operations, eg. asynchronous callbacks.
Can we avoid this? If we are working in C# 4 or above, we can. And we can achieve this by using – Lazy.
Lazy implementation
Lazy initialization of an object means that object creation is deferred until it is first used. This way of object construction is used primarily for optimization purposes. In .NET 4 and above, we can use the Lazy class, which encapsulates this kind of behavior. It is also thread-safe, so we don’t have to worry about multiple threads creating duplicate objects.
So, Singleton implementation using Lazy would look like this:
using System; namespace SingletonExamples { public class SingletonLazy { private static readonly Lazy<SingletonLazy> instance = new Lazy<SingletonLazy>(() => new SingletonLazy()); public static SingletonLazy Instance { get { return instance.Value; } } private SingletonLazy() { } } }
Conclusion
Singleton is for sure one pattern with many flaws. The problem is also that this pattern is often used in places not quite suited for it, thus coupling code, and adding performance issues. However, it has its purpose and its (rare) use. And althought that there are multiple variations of Singleton implementation, I personally, prefer the last version of implementation. It is simplest, thread-safe solution with lazy initialization, that everyone can understand. Also, I need to add once again that first implementation shouldn’t be used ever.
Andriy Kravets is writer and experience .NET developer and like .NET for regular development. He likes to build cross-platform libraries/software with .NET.