How to abstract a singleton class?
Adding to BTownTKD's answer, it's actually pretty simple to restrict a constructor call in runtime (not sure possible in compilation). All you do is add a protected constructor in SingletonBase, that throws an exception if _instance is not null. The exception will be thrown even if the constructor is the first thing to be called from outside.
I managed to apply this technic in a singleton base, and also make it lazy and thread safe as described here: http://csharpindepth.com/Articles/General/Singleton.aspx
The result (with usage explanation):
/// <summary>
/// Generic singleton class, providing the Instance property, and preventing manual construction.
/// Designed as a base for inheritance trees of lazy, thread-safe, singleton classes.
/// Usage:
/// 1. Sub-class must use itself, or its sub-class, as the type parameter S.
/// 2. Sub-class must have a public default constructor (or no constructors).
/// 3. Sub-class might be abstract, which requires it to be generic and demand the generic type
/// have a default constructor. Its sub-classes must answer all these requirements as well.
/// 4. The instance is accessed by the Instance getter. Using a constructor causes an exception.
/// 5. Accessing the Instance property in an inner initialization in a sub-class constructor
/// might cause an exception is some environments.
/// </summary>
/// <typeparam name="S">Lowest sub-class type.</typeparam>
public abstract class Singleton<S> where S : Singleton<S>, new()
{
private static bool IsInstanceCreated = false;
private static readonly Lazy<S> LazyInstance = new Lazy<S>(() =>
{
S instance = new S();
IsInstanceCreated = true;
return instance;
});
protected Singleton()
{
if (IsInstanceCreated)
{
throw new InvalidOperationException("Constructing a " + typeof(S).Name +
" manually is not allowed, use the Instance property.");
}
}
public static S Instance
{
get
{
return LazyInstance.Value;
}
}
}
I must say I haven't done intensive multi-threading testing, but as some already said, you can always use the old double-check trick.
The true solution is starting with BTownTKD's approach but augmenting it with the Activator.CreateInstance method which allows your child classes to keep the private constructors.
Parent Class
public abstract class SingletonBase<T> where T : SingletonBase<T>
{
private static readonly Lazy<T> Lazy =
new Lazy<T>(() => Activator.CreateInstance(typeof(T), true) as T);
public static T Instance => Lazy.Value;
}
Child Class
public sealed class MySingleton : SingletonBase<MySingleton>
{
private MySingleton() { }
}
Full Implementation Example Here
You can achieve this using a combination of a self-referencing generic type constraint, and a "new()" type constraint.
The "new" constraint ensures that any child class will always have a parameterless constructor, so _instance = new T();
will always work.
The self-referencing type constraint ensures that the "Instance" static property always returns the correct Type; not the "base" type. Your singleton base class would look something like this:
public abstract class SingletonBase<T>
where T : SingletonBase<T>, new()
{
private static T _instance = new T();
public static T Instance
{
get
{
return _instance;
}
}
}
Your child classes will look like this:
public class MyChildSingleton : SingletonBase<MyChildSingleton>
{
//Done!
}
Of course, if you want your singleton to be general-purpose, you should also change your "create singleton instance" code slightly, to use the "double-check lock" pattern, or the Lazy class, to make it thread-safe.
The big caveat: if you use this method, the "new()" constraint pretty much ensures that your class will always have a public, parameterless constructor. That means your end-users could always just call new MyChildSingleton()
if they really wanted, bypassing your singleton instance entirely. Your singleton would be "by convention," instead of strictly enforced. To get around this would take a bit more engineering. In the above scenario, the convention seems to be that you should name your static instance "Default
" instead of "Instance
." This subtly conveys the fact that your class offers a 'suggested' singleton instance, but using it is technically optional.
I've made some attempts to strictly enforce the singleton pattern, and the end result was to use reflection to manually invoke a private constructor. You can see my full code attempt here.