Generic and Threadsafe Singleton Implementation
February 6th, 2006
I googled around, and couldn’t find a generic singleton implementation that was 1) correct and 2) met all of my needs. This is a clever approach, but unfortunately it is limited to objects that are created by calls to constructors.
So I went ahead and wrote the singleton generic that I fully expect to be included in the next BCL. (Not really. It’s completely straightforward. However, the Steelers won the super bowl yesterday, so I’m not really in a modest mood.)
I started out defining a class factory type, which is responsible for creating the an instance in a purposefully vague way. I also wrote the default version which just calls new().
/// <summary> /// Interface for objects that create instances of /// another type. /// </summary> public interface IClassFactory<T> where T : class { T CreateInstance(); } /// <summary> /// A default <see cref="IClassFactory"/> implementation, /// which uses a parameterless constructor to create the /// instance. /// </summary> public class DefaultClassFactory<T> : IClassFactory<T> where T : class, new() { public T CreateInstance() { return new T(); } }
From there, I went for the slam dunk in writing both a singleton class with a class factory and a default version that doesn’t require one.
/// <summary> /// A base (or helper) singleton class. Defines the /// singleton instance. /// </summary> /// <typeparam name="T"> /// The type of the singleton object. /// </typeparam> /// <typeparam name="class_factory"> /// The type of the class factory to use to create an /// instance of type <typeparamref name="T"/>. /// </typeparam> public class Singleton<T, class_factory> where T : class where class_factory : IClassFactory<T>, new() { private static object _sync = new object(); private static T _default; /// <summary> /// Gets the singleton instance. /// </summary> public static T Default { get { EnsureDefault(); return _default; } } /// <summary> /// Ensures that the singleton has been created. /// </summary> private static void EnsureDefault() { if (_default == null) { lock (_sync) { if (_default == null) { CreateDefault(); } } } } /// <summary> /// Uses the class factory to create the instance. /// </summary> private static void CreateDefault() { class_factory cf = new class_factory(); T value = cf.CreateInstance(); // This ensures that writes in the creation of // the default instance won't be shuffled beyond // the write to _default. Only matters on multiproc // machines where the hardware allows this. Does // nothing on an x86. Thread.MemoryBarrier(); _default = value; } } /// <summary> /// A basic singleton type that can be used with objects /// that are created with a parameterless constructor. /// </summary> public class Singleton<T> : Singleton<T, DefaultClassFactory<T>> where T : class, new() { }
(For more on why I added that call to Thread.MemoryBarrier(), see this writeup.)
Here’s a really simple example that does not take advantage of the customizability:
public class Foo : Singleton<Foo>
{
}
If you can’t use Singleton as a base class, you have to write a little more code.
public class Foo : Bar
{
public static Foo Default
{
get { return Singleton<Foo>.Default; }
}
}