Today while browsing through new projects in codeplex I found this gem. I haven't seen it before and so felt that I should blog about it.
Almost all of us would be aware of the singleton pattern and where it comes handy. While I love it and use it wherever applicable, there is also a bit of uneasiness when I had to implement the double locking syntax for each singleton class... I wondered whether something can be done to simplify this but I got nowhere.
Now, I've found a solution for this problem in this project. I'm giving below a simplified version of the Singleton factory found in the project, with example...
namespace Test
{
// this is the class for which I want to maintain a single instance
public class MyClass
{
private MyClass()
{
// private constructor ensures that callers cannot instantiate an object using new()
}
}
// Singleton factory implementation
public static class Singleton<T> where T : class
{
// static constructor, runtime ensures thread safety
static Singleton()
{
// create the single instance of the type T using reflection
Instance = (T)Activator.CreateInstance(typeof(T), true);
}
// serve the single instance to callers
public static T Instance { private set; get; }
}
class Program
{
public static void Main()
{
// test
Console.WriteLine(Object.ReferenceEquals(Singleton<MyClass>.Instance, Singleton<MyClass>.Instance));
}
}
}
The code is self explanatory. Now, whenever I want a singleton instance of a class, all I have to do is Singleton<ClassTypeName>.Instance and the magic happens. Isn't this a beauty? Full credit to Tan Chun Mun.
15 comments:
You should try to implement ISerializable interface also to ensure serializing / deserializing also uses the singleton...
That looks pretty cool. Just a small recommendation. Instead of using Activator.CreateInstance I would put a generic restrictor (where T = new()) and simply do new T(). Both the method in your sample and here assume that the singleton class will have a paramerless constructor so a (where T = new()) is a good idea for catching potential errors at compile time.
Cheers,
Anastasiosyal
Anastasiosyal
What about Instance = default(T) instead of Instance = (T)Activator.CreateInstance(typeof(T), true)?
This is simply a generic implementation of this:
http://www.yoda.arachsys.com/csharp/threads/
The solution to adding parameters and providing a simple means to override the construction in arbitrary ways is shown here: http://aabs.wordpress.com/2007/11/21/a-generic-class-factory-for-c-30/
@anastasiosyal: we can't use new() since the constructor is private. private constructor is a typical requirement of singleton classes.
@Chris O: default(T) will set the instance value as null
Hey Shiva !
Nice example, but what Chris o. says is possible...
We have current code running in our project
public abstract class SingletonBase<T> where T : new()
{
private static T s_Instance;
static SingletonBase()
{
s_Instance = new T();
}
public static T Instance
{
get
{
return s_Instance;
}
}
}
public class SingletonExample : SingletonBase<SingletonExample>
{
}
Difference here is that your own derived class inherits the Singleton class.
Depechie,
but then SingletonExample can not have a private default constructor so how do you guarantee that nobody creates more SingletonExample instances...
Anonymous and Shiva... I see what you mean and yes Shiva's solution is better that way :)
With Instance = System
.Runtime
.Serialization
.FormatterServices
.GetUninitializedObject(typeof(T))
as T; you can create an instance with a private constructor. Also it is faster than Activator.CreateInstance.
@kai: GetUninitializedObject() will not invoke the constructor on the object that we want to use as a singleton. Clearly, this is not what we want!
@shiva: You're right. What a mistake. The name of the method says everything!
Another try:
using System.Reflection;
...
Instance = typeof(T)
.InvokeMember(typeof(T).Name,
BindingFlags.CreateInstance |
BindingFlags.Instance |
BindingFlags.NonPublic|
BindingFlags.Public,
null, null, null) as T;
@kai: sure that would work
using System.Reflection;
...
Instance = typeof(T)
.InvokeMember(typeof(T).Name,
BindingFlags.CreateInstance |
BindingFlags.Instance |
BindingFlags.NonPublic|
BindingFlags.Public,
null, null, null) as T;
This is not working in WinCE or handheld application. but Activator.CreateInstance and new is working in WinCE and handheld application. From my perception, This why the previous author use it.
Post a Comment