on
The Singleton Pattern – Revised
While in a previous post it is explained how the singleton pattern can be implemented using double checked locking or a static constructor, .NET 4.0 provides a dedicated class Lazy<T> to support lazy initialization and the singleton pattern. This class is pretty well documented at its MSDN site , however I have found the documentation a bit over-complicated. So I would like to introduce a simpler summary of this class’s behavior.The general usage of the Lazy<T> class is pretty simple. Given a type parameter T, the Value property of an object of type Lazy<T> returns an initialized instance of T. The exact execution of the initialization process, that has a major impact in a multi-threaded environment, is controlled by the constructor parameters of the Lazy<T> class:
- the isThreadSafe and mode arguments control the behavior of object initialization
- the valueFactory argument affects how the object of type T is constructed and also strongly influences how exceptions are handled
The effect of the isThreadSafe and mode parameters are concluded in the table below:
The constructor of the object T is called at most once, and the Value property of Lazy<T> returns the same object for all calls |
mode = ExecutionAndPublication, or isThreadSafe = true |
The constructor of the object T might get called multiple times, but the Value property of Lazy<T> returns the same object for all calls. (Additionally created objects are discarded.) | mode = PublicationOnly |
The constructor of the object T might get called multiple times, and the Value property of Lazy<T> might return different object references for different calls. |
mode = None, or isThreadSafe = false |
There are two interesting edge cases when Lazy<T> does not act like expected. When the valueFactory is set and whether the isThreadSafe parameter is set to false or the mode is set to None then calling the Value property might throw an InvalidOperationException with the message “ValueFactory attempted to access the Value property of this instance”. This happens when the Value property is read concurently prior having the object initialized; that is both call tries to initialize a new object of T. Note that no exception is raised when one of the threads constructs the object before the other would access the property Value. All in all, this type of uncertainty is to be avoided, so never use the Lazy<T> class in a multi-threaded environment with the isThreadSafe parameter set to false or the mode set to None.
Besides how the Lazy<T> class behaves in a multi-threaded environment, it is important to know how exceptions are handled. It is not that difficult; there are two ways of exception handling depending on the presence of the valueFactory parameter and the thread safety parameters.
- In case exceptions are cached, once an exception is thrown at initialization all the following tries receive the same exception. No matter if those calls could succeed. That is, initialization is a one-shoot process. If it fails all calls of Value throws the same exception that was raised at its first call.
- When exceptions are not cached, every single read of the Value property throws a new exception until the initialization succeeds.
Exceptions are cached | valueFactory is set and mode is not PublicationOnly |
Exceptions are not cached | valueFactory is not set or mode is PublicationOnly |
Here the important point is that with cached exceptions once the object initialization fails it is not reinvoked. All consecutive reads of Value throw the same exception. No matter if later the initialization becomes feasible, Value will never return an object.