Introduction
Many people from different programming
backgrounds should be familiar with the Singleton Pattern.
Those who use it will find that they often have to write
the same code each time they want to create a different
Singleton class. With the advent of C# 2.0 Generics, it
is possible to write this code only once.
Background
There are many articles about the Singleton
Pattern. Probably the most comprehensive one for C# can
be found here: "Implementing the Singleton Pattern
in C#".
There is an increasing amount written about
C# Generics. For example, a CodeProject article can be found
here: "Generics in C# 2.0" by Ansil.
Using C# 2.0 Generics to
achieve a reusable Singleton pattern
Using C# 2.0 Generics, it is possible to
create what I have called a 'Singleton Provider'. This is
a class that can be used repeatedly to instantiate a class
as a singleton without having to re-write the singleton
pattern code for that specific class. This has the added
benefit of separating singleton code from the class code
leaving the flexibility to use several instances of the
class or using the class as a singleton.
The singleton code used in this example
is based on the fifth example described in the above article
about implementing the Singleton Pattern in C#:
public sealed class Singleton
{
Singleton()
{
}
public static Singleton Instance
{
get
{
return SingletonCreator.instance;
}
}
class SingletonCreator
{
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static Nested()
{
}
internal static readonly Singleton instance = new Singleton();
}
}
With an understanding of Generics, you
can see that there should be no reason to replace the type
arguments in this block of code with the typical 'T '
found in Generics. If this is done, the code looks like
this.
public class SingletonProvider <T> where T:new()
{
SingletonProvider() {}
public static T Instance
{
get { return SingletonCreator.instance; }
}
class SingletonCreator
{
static SingletonCreator() { }
internal static readonly T instance = new T();
}
}
Note that the Generics must have a constraint
on it. This constraint forces any type 'T '
to have a default constructor, that is, a constructor that
takes no parameters. This allows the SingletonCreator
to instantiate the type 'T '.
So, how does one use the SingletonProvider ?
To understand how to use this, we need a test class. The
test class has two features. The first is a default constructor
that sets a timestamp member variable. The second is a public
method that writes that timestamp using Debug.WriteLine .
This setup means that no matter which thread uses this class
in the Singleton Pattern, whenever that public method is
called, it should output the same value.
public class TestClass
{
private string _createdTimestamp;
public TestClass ()
{
_createdTimestamp = DateTime.Now.ToString();
}
public void Write()
{
Debug.WriteLine(_createdTimestamp);
}
}
The class is used with the SingletonProvider
as follows:
SingletonProvider<TestClass>.Instance.Write();
Points of Interest
I have tested this code with a Dual Processor
with hyper-threading enabled with 100 threads accessing
the singleton. They all output the same value illustrating
that this is a thread-safe generic way to create singletons.
I believe that this is a really neat illustration
of how Generics can save you writing code. |