Andy's observations as he continues to attempt to know all that is .NET...

Wednesday, February 13, 2008

True Generic Singleton

Last weekend I was teaching Code Smarter with Design Patterns in .NET, yep that's right a weekend...The guys at Intelliflo decided they didn't have time during the regular week for training that wanted me to pack a 4 day course into 3 days over the weekend so we worked from 9 until 19:00...


 

Whilst teaching them the Singleton pattern they bought out a version they were using built on Generics.


 


public
class
AlmostSingleton<T> where T:new()

{


private
static T instance;


private
static
object initLock = new
object();


 


public
static T GetInstance()

{


if (instance == null)

{

CreateInstance();

}


 


return instance;

}


 


private
static
void CreateInstance()

{


lock (initLock)

{


if (instance == null)

{

instance = new T();

}

}

}

}


 

public
class
Highlander : AlmostSingleton<Highlander>

{


public Highlander()

{


Console.WriteLine("There can be only one...");

}

}


 

static
void Main(string[] args)

{


Highlander highlander = Highlander.GetInstance();


Highlander highlander2 = Highlander.GetInstance();


 


Debug.Assert(object.ReferenceEquals(highlander, highlander2));

}


 

A bit of a google around and it appears a few people are advocating similar solutions. On the surface all looks well, but to be really true to the singleton pattern the type which is the singleton should only ever have a single instance. This implementation requires T to have a public constructor, so that whilst it is certainly possible to always get back to a common instance via the GetInstance method, what is lacking and what the singleton pattern strictly requires is the enforcing of only a single instance of the type. Since the fact that the type has a public constructor means that another client could easily create an instance as opposed to calling GetInstance().


 

static
void Main(string[] args)

{


Highlander highlander = Highlander.GetInstance();


Highlander highlander2 = Highlander.GetInstance();


 


Debug.Assert(object.ReferenceEquals(highlander, highlander2));


 

// This will create a second instance of Highlander..

// not what we want support


 


Highlander highlander3 = new
Highlander();

}


 


 

In fact supporting the true singleton behaviour isn't too hard, it simply requires the use of a bit of reflection to create the object instance rather than rely on the typical language new construct.


 

public
class
Highlander : Singleton<Highlander>

{


private Highlander()

{


Console.WriteLine("There can be only one...");

}

}


 

public
class
Singleton<T>

{


private
static T instance;


private
static
object initLock = new
object();


 


public
static T GetInstance()

{


if (instance == null)

{

CreateInstance();

}


 


return instance;

}


 


private
static
void CreateInstance()

{


lock (initLock)

{


if (instance == null)

{


Type t = typeof(T);


 


// Ensure there are no public constructors...


 


ConstructorInfo[] ctors = t.GetConstructors();


if (ctors.Length > 0)

{


throw
new
InvalidOperationException(

String.Format("{0} has at least one accesible ctor making it impossible to enforce singleton behaviour",

t.Name));

}


 


// Create an instance via the private constructor

instance = (T)Activator.CreateInstance(t, true);

}

}

}

}


 

Now any attempt to create the instance outside the Highlander class using the new keyword would produce a compiler error, thus enforcing the true singleton pattern. I've also added a guard to make sure that when you use the Generic wrapper it ensures that you do not have a public constructor which would ultimately allow a client to by bass the singleton functionality.


 


 

9 comments:

Simon Hughes said...

Not bad, but you need to make a slight alteration to it.

declare the class as follows:

public class Singleton < T > where T : class
{
...
}

Andy Clymer said...

Thanks for that catch Simon. You are absolutly correct, in order to make sure some one doesn't accidently try and make a value type a singleton.

Chris Smith said...

Comments from the original author of the singleton....

Good point about value types. That would be pointless. Class declaration should actually be, under that circumstance:

public abstract class Singleton< T > where T : class, new()
{
...
}

The new constraint is there to enforce the fact that a singleton should be able to be instantiated stateless and obtain its internal state in the constructor as well as the fact that you cannot create an instance of a generic with a parameterized constructor.

As you point out the downside is that the object can be instantiated on its own without the singleton. This is not all negative however due to the following:

Testing is easier as instances don't have to go through the singleton, meaning that you don't have to blow away the appdomain on successive test cases to exercise functionality on clean instances.

The incorrect usage would not escape one of my code reviews anyway :-)

Andy Clymer said...

Thanks Chris for that ease of testing is soemthing I hadn't considered.

So here's some initial thoughts.

If testing is a primary focus, singletons certainly create their own testing nightmare, e.g lack of ability to mock them.

However there is certainly a work around, and that would be to have a different singleton wrapper for unit testing, that has an additional method that allowed reseting of the Singleton instance.

That way you would prevent the missuse of the class in production before code review.

In the end its what ever makes you feel comfortable.

Chris said...

That's an interesting thought. I will have a look at that.

The more automated the review process is (or the less to review) the better.

Thanks for the suggestion.

Magnus Martensson said...

Hi Andy! I just posted another Singleton version that does not force you to inherit Singleton. Naturally it is not exactly the same thing but it is something similar and based on your thoughts!

Generic Singleton behavior wich does not force inheritance

Cheers,

/Magnus

Magnus MÃ¥rtensson said...

And I totally misspelled my link! ;~) Here's the right one!

Generic Singleton behavior which does not force inheritance

Toke Boisen said...

A question:

Why not use the generic version of Activator.CreateInstance?

instance = Activator.CreateInstance< T >();

instead of

instance = (T)Activator.CreateInstance(t, true);

Andy Clymer said...

Toke,

You can't use the generic method version as it doesn't allow you to say use private constructors. We need that behaviour since having a public constructor on the type would allow multiple creations of the object something we are trying to avoid.

About Me

My photo
Im a freelance consultant for .NET based technology. My last real job, was at Cisco System were I was a lead architect for Cisco's identity solutions. I arrived at Cisco via aquisition and prior to that worked in small startups. The startup culture is what appeals to me, and thats why I finally left Cisco after seven years.....I now filll my time through a combination of consultancy and teaching for Developmentor...and working on insane startups that nobody with an ounce of sense would look twice at...