Introduction
This article presents a generic class that uses Reflection to create a single instance of any class. It can be used to instantiate Singletons from public classes with non-public constructors, and Singletons from non-public classes as well.
The main motivation for this solution is to have the Singleton pattern written in only one place, reusable by all classes that need to follow that pattern.
Simply use Singleton<T>.Instance
to get a Singleton instance.
Background
There are already very good articles (Lazy vs. Eager Init Singletons / Double-Check Lock Pattern and Implementing the Singleton Pattern in C#) that describe the challenges and caveats of developing a Singleton in .NET; I won't go into the details again. There are also some Singletons using Generics here and there (see Generic Singleton Provider as an example), but none were solving the problem of creating a single instance of a class with a non-accessible constructor (something that all Singletons should have).
Design
Singleton<T>
uses the Double-Check Lock Pattern rather than static type initialization. The motivation behind this choice is that error-recovery and retries after a type-initializer failure are impossible in .NET (once a type-initializer fails, trying to create a class instance will always throw an exception, even if the error was meanwhile resolved). The chosen implementation strategy provides more flexibility for better error recovery.
Singleton<T>
also forces the target class constructor to be private
or protected
to prevent casual class instantiation.
Singleton<T>
This is the generic Singleton
class:
public static class Singleton<T>
where T : class
{
static volatile T _instance;
static object _lock = new object();
static Singleton()
{
}
public static T Instance
{
get
{
if (_instance == null)
lock (_lock)
{
if (_instance == null)
{
ConstructorInfo constructor = null;
try
{
constructor = typeof(T).GetConstructor(BindingFlags.Instance |
BindingFlags.NonPublic, null, new Type[0], null);
}
catch (Exception exception)
{
throw new SingletonException(exception);
}
if (constructor == null || constructor.IsAssembly)
throw new SingletonException(string.Format("A private or " +
"protected constructor is missing for '{0}'.", typeof(T).Name));
_instance = (T)constructor.Invoke(null);
}
}
return _instance;
}
}
}
The type parameter T
represents the class that must be instantiated like a Singleton.
Singleton<T>
uses the class
constraint to force the type parameter to be a reference type. The new()
constraint, which forces the type parameter to have a public parameterless constructor, is not used here, because the goal of the generic class is to provide single instance creation for classes with non-public constructors.
_instance
is defined using the volatile
keyword, and a dummy object, _lock
, is used to synchronize threads with the double-check lock pattern. See Lazy vs. Eager Init Singletons / Double-Check Lock Pattern for the details of this strategy.
Singleton<T>
has a static constructor: it simply makes sure that static fields initialization occurs when the Instance
property is called the first time and not before.
The initialization of the _instance
field is made by Reflection using the type parameter. typeof(T)
first gets the type of the type parameter, then GetConstructor
gets its constructor, and Invoke
finally returns the new instance. The BindingFlags
tells GetConstructor
to search the constructor in the non-public (BindingFlags.NonPublic
) instance members (BindingFlags.Instance
) of the type. The search doesn't include public
members, and the last if
statement excludes internal
constructors, as it is a best practice to keep Singleton constructors private
or protected
(you don't want anybody else to instantiate these classes).
As you can presume, GetConstructor
can throw an exception if the type parameter is not a class, as the class
constraint also allows interface, delegate, or array types. There's just so much you can do with these constraints.
Using Singleton<T>
Another best practice of the Singleton pattern is to have every Singleton return its own single instance. This is usually done by a static property named Instance
. The following code snippet shows how to use Singleton<T>
in a property.
public static SingleInstanceClass Instance
{
get
{
return Singleton<SingleInstanceClass>.Instance;
}
}
Demo
The demo shows the life cycle of the SingleInstanceClass
singleton. The code is self-explanatory:
public class SingleInstanceClass
{
private SingleInstanceClass()
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("SingleInstanceClass created.");
Console.ResetColor();
_count++;
}
public static SingleInstanceClass Instance
{
get { return Singleton<SingleInstanceClass>.Instance; }
}
public static int Count { get { return _count; } }
public static void DoStatic()
{ Console.WriteLine("DoStatic() called."); }
public void DoInstance()
{ Console.WriteLine("DoInstance() called."); }
private static int _count = 0;
}
class Program
{
static void Main()
{
Console.WriteLine("---");
Console.WriteLine("Initial instance count: {0}",
SingleInstanceClass.Count);
Console.WriteLine("---");
Console.WriteLine("Calling DoStatic()");
SingleInstanceClass.DoStatic();
Console.WriteLine("Instance count after DoStatic(): {0}",
SingleInstanceClass.Count);
Console.WriteLine("---");
Console.WriteLine("Calling DoInstance()");
SingleInstanceClass.Instance.DoInstance();
Console.WriteLine("Instance count after first DoInstance(): {0}",
SingleInstanceClass.Count);
Console.WriteLine("---");
Console.WriteLine("Calling DoInstance()");
SingleInstanceClass.Instance.DoInstance();
Console.WriteLine("Instance count after second DoInstance(): {0}",
SingleInstanceClass.Count);
Console.ReadKey();
}
}
The program output is:
Conclusion
There are many ways to implement the Singleton pattern in C#. Singleton<T>
uses Reflection to make it a write-once-for-all solution.
History
- 2009-04-18: Refactored to use the double-check lock pattern and the
volatile
keyword rather than a type-initializer. Also added SingletonException
. - 2009-06-07: Replaced
typeof(T).InvokeMember
with typeof(T).GetConstructor
to support the Compact Framework.