Using generics is not useful for singletons. Because you can always create multiple instances of the type parameter T and then it is not by definition a singleton.
Look at this:
public sealed class Singleton<T> where T : class, new()
{
private static readonly Lazy<T> instance = new Lazy<T>(() => new T());
public static T Instance => instance.Value;
private Singleton() { }
}
And when you use it like this
public class Adapter
{
public static Adapter Instance => Singleton<Adapter>.Instance;
// private Adapter(){ } // doesn't compile.
}
you can still create Adapters yourself just call
new Adapter();
Adding the private constructor to Adapter breaks the code.
Note that this Adapter uses composition not inheritance. I.e. it is not a Singleton it has a Singleton. If something is a Singleton it should derive from or implement an interface.
public abstract class Singleton<T>
{
protected static Lazy<T> instance;
public static T Instance => instance.Value;
}
public sealed class Adapter : Singleton<Adapter>
{
static Adapter()
{
instance = new Lazy<Adapter>(() => new Adapter());
}
private Adapter() { }
}
Basically this only moves a static field into a generic base class while it is no longer readonly and therefore can be changed after initialization. Also it requires you to remember to add the private constructor, mark it as sealed and perform some initialization so it still isn't well encapsulated and prone to mistakes.
We can improve this by adding a check in the base constructor.
public abstract class Singleton<T> where T : Singleton<T>, new()
{
private static bool instantiated;
private static readonly Lazy<T> instance = new Lazy<T>(() => new T());
public static T Instance => instance.Value;
protected Singleton()
{
if (instantiated)
throw new Exception();
instantiated = true;
}
}
public /* sealed */ class Adapter : Singleton<Adapter>
{
}
Due to the fact that the Singleton is lazy the pattern is still broken.
new Adapter(); // this works
Adapter.Instance; // this throws an error.
also
Adapter.Instance; // this works
// just running in production someone decided to call:
new Adapter(); // this throws an error
These kind of error's can only be detected during testing.
I would still prefer to just use the pattern directly it's basically just 3 lines of code and it is compile time safe. Also when you need a different base classes but still need a lazy singleton and require it to be a true singleton a simple solution exists: (see: http://csharpindepth.com/Articles/General/Singleton.aspx)
public sealed class Adapter
{
private static readonly Lazy<Adapter> instance = new Lazy<Adapter>(() => new Adapter());
public static Adapter Instance { get { return instance.Value; } }
private Adapter() { }
}
You cannot refactor this properly into a generic singleton that cannot be misused like the above investigated approaches.