4
\$\begingroup\$

I did this as an exercise just to practice/improve using generics.

Independent of how useful this implementation of a Singleton is, how is my coding in terms of using generics and any other aspect of class design of code style?

void Main()
{
    var a = Singleton<MyClass>.Value;
    var b = Singleton<MyClass, MyClassFactory>.Value;
    var c = Singleton<MyClass>.Value;
    var d = Singleton<MyClass, MyClassFactory>.Value;
    var e = Singleton<MyOtherClass>.Value;
    var f = Singleton<MyOtherClass>.Value;
    var g = Singleton<MyOtherClass, MyOtherFactory>.Value;
    var h = Singleton<MyOtherClass, MyOtherFactory>.Value;
}

class SingletonBase
{
    protected static object Locker = new LockerObject();
}

class Singleton<T> : SingletonBase where T : new() 
{
    static T StaticT;

    public static T Value 
    {
        get
        {
            lock (Locker)
            {
                if(StaticT == null)
                {
                    StaticT = Activator.CreateInstance<Factory<T>>().Create();
                }
                else
                {
                    Console.WriteLine ("Singleton<T>::Value" + typeof(T).Name + " is already created");
                }
            }
            return StaticT;
        }
    }
}

class Singleton<T, F> : SingletonBase where T : new() where F : IFactory<T>, new()
{
    static T StaticT;

    public static T Value 
    {
        get
        {
            lock (Locker)
            {
                if(StaticT == null)
                {
                    StaticT = new F().Create();
                }
                else
                {
                    Console.WriteLine ("Singleton<T, F>::Value" + typeof(T).Name + " is already created");
                }
            }
            return StaticT;
        }
    }
}

class LockerObject
{
    Guid myGUID;
    public LockerObject()
    {
        this.myGUID = Guid.NewGuid();
        Console.WriteLine ("New LockerObject " + this.myGUID.ToString());
    }
}

interface IFactory<T>
{
    T Create();
}

class Factory<T> : IFactory<T> where T : new()
{
    public T Create()
    {
        Console.WriteLine ("Factory<T>::Create()");  
        return new T();
    }
}

class MyClassFactory : IFactory<MyClass>
{
    public MyClass Create()
    {
        Console.WriteLine ("MyClassFactory::Create()");    
        return new MyClass();
    }
}

class MyClass
{
    public MyClass()
    {
        Console.WriteLine ("MyClass created");
    }
}


class MyOtherClass
{
    public MyOtherClass()
    {
        Console.WriteLine ("MyOtherClass created");
    }
}

class MyOtherFactory : IFactory<MyOtherClass>
{
    public MyOtherClass Create()
    {
        Console.WriteLine ("MyOtherFactory::Create()");    
        return new MyOtherClass();
    }
}

Output:

New LockerObject 36aa2282-d745-43ca-84d2-998a78e39d51
Factory<T>::Create()
MyClass created
MyClassFactory::Create()
MyClass created
Singleton<T>::ValueMyClass is already created
Singleton<T, F>::ValueMyClass is already created
\$\endgroup\$
  • 2
    \$\begingroup\$ a little offtopic: those are not singletons. As long as class T is required to have a public parameterless constructor, anyone can create a new instance of it. Same problem with the version using IFactory<T>. \$\endgroup\$ – Cristian Lupascu May 23 '12 at 7:47
  • \$\begingroup\$ understood, good point. I was only designing it for code that makes the assumption that a DI container is able to control all object instantiation... \$\endgroup\$ – Aaron Anodide May 23 '12 at 15:34
  • \$\begingroup\$ Does this design assure in multithreading case it creates only one object of this class? \$\endgroup\$ – Prasad S Deshpande Oct 17 '12 at 4:45
  • \$\begingroup\$ Have a read of csharpindepth.com/Articles/General/Singleton.aspx \$\endgroup\$ – dreza Jul 7 '13 at 20:33
3
\$\begingroup\$

About your locking strategy to create the singleton, I would use a double check to avoid too much locking:

if(x)
{
    lock(Locker)
        if(x) //again once we got the lock
            DoStuff();
} 
else 
{
    FooBar();
}

Like this, you will hopefully use the lock only once when you first call the instance.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.