The Singleton pattern is one of the simplest design patterns, which restricts the instantiation of a class to ONLY ONE object. A singleton class only allows a single instance of itself to be created, and usually gives simple access to that instance. Most commonly, singletons don‘t allow any parameters to be specified when creating the instance.
In this article, we would provide different ways of implementing the singleton pattern using C# and discuss the difference in terms of lazily-load, thread-safety, and perfromance.
Singleton and Static Class
Before describing the implementations, we clarify the difference between singletons and static classes. We can make a global single class by using keyword static. Both singleton and static class have only one instance of them. The static classes are usually used for storing global data, all the data in a static class are also static. A singleton is treated as a normal class (without static keyword) with state, which allows you to reuse code and control object state much easier. We can extend classes or implement interfaces with singletons but not with static classes. Aslo, a singleton is allocated in heap and a static class is allocated in stack.
Instantiation: Lazy vs Eager
// Implementation 1: lazy instantiation, not thread-safe! Do not use! public class Singleton1 { private static Singleton1 mInstance = null; // Private constructor private Singleton1() { } // GetInstance public static Singleton1 Instance { get { if (mInstance==null) { mInstance = new Singleton1(); } return mInstance; } } } // Implementation 2: eager instantiation, simple thread-safe without using locks public sealed class Singleton2 { private static readonly Singleton2 mInstance = new Singleton2(); // Private constructor private Singleton2() { } // GetInstance public static Singleton2 Instance { get { return mInstance; } } }
The Singleton1 implementation is bad because it is not thread-safe. Two different threads could both pass the if test when the mInstance is null, then both of them create instances, which violates the singleton pattern. The advantage of this implementation is the instance is created inside the Instance property method, the class can exercise additional functionality. The instantiation is not performed until an object asks for an instance, so called "Lazy Instantiation", the lazy instantiation avoids instantiating unnecessary singletons when the application starts.
The Singleton2 implementation is an eager initialization, which will always create an instance. In this implementation, the instance is created the first time any member of the class is referenced, the common language runtime takes care of the variable initialization. The class is marked sealed to prevent derivation, which could add instances. The instance variable is marked readonly which means that it can be assigned only during static initialization or in a class constructor. Hence, the *private* constructor ensures that the instance variable can be instantiated only inside the the class and therefore only one instance can exist in the system. The downside of this implementation is that you have less control over the mechanics of the instantiation.
Thread-safety
// Implementation 3: thread-safe with locking the shared object public sealed class Singleton3 { private static Singleton3 mInstance = null; private static readonly object SingletonLock = new object(); Singleton3() { } public static Singleton3 Instance { get { lock (SingletonLock) { if (mInstance == null) { mInstance = new Singleton(); } return mInstance; } } } }
In the Singleton3 implementation, all threads share a lock object to ensure that only one thread will create an instance, because only the first thread entering in the critical section can find the instance variable is null and pass the if check. The withdraw of this implementation is obvious, the performance suffers as a lock is acquired every time the instance is requested.
The Singleton4 implementation improve the performance by avoiding the unnecessary lock operator. To do so, it uses a double null-check to avoid taking out a lock every time. However, this implementation doesn‘t work in Java and is easy to get wrong.
// Implementation 4: double null-check. Bad code! Do not use! public sealed class Singleton4 { private static Singleton4 mInstance = null; private static readonly object SingletonLock = new object(); Singleton() { } public static Singleton4 Instance { get { if (mInstance == null) { lock (SingletonLock) { if (mInstance == null) { mInstance = new Singleton4(); } } } return mInstance; } } }
Laziness and Performance
To be fully lazy instantiated, Singleton5 implementation uses a nested class in the singleton class. Therefore, the instantiation is triggered the first time the nested class is referenced which could only occur in Instance. This implementation is fully lazy and has better performance than previous implementations.
// Implementation5: fully lazy using nested classpublic sealed class Singleton5 { private Singleton() { } public static Singleton5 Instance { get { return Nested.instance; } } private class Nested { static Nested() { } // Make the instantiation internal static readonly Singleton instance = new Singleton5(); } }
If you‘re using .NET 4 or higher, you can use the System.Lazy<T> type to make the laziness really simple. As shown in Singleton6 implementation, all you need to do is pass a delegate to the constructor which calls the Singleton constructor - which is done most easily with a lambda expression. It‘s simple and performs well. Aslo, you can check whether the instance has been created with the IsValueCreated property.
// Implementation6: fully lazy, .NET 4 or higher only public sealed class Singleton6 { private static readonly Lazy<Singleton> lazy = new Lazy<Singleton>(() => new Singleton6()); private Singleton() { } public static Singleton Instance { get { return lazy.Value; } } }
References:
http://csharpindepth.com/articles/general/singleton.aspx
http://msdn.microsoft.com/en-us/library/ff650316.aspx