One singleton can be an extremely valuable tool for
enhancing performance. This is particularly true when there is always just one
physical object. In traditional programming, you would instantiate a class as
an object every time you need a specific instance of an object. For instance,
you would create your own product “12” object if you needed access to
the information about product “12.”

However, in many cases, such as the case in a product
catalog, there is one, single persistence of the object in the database, and
therefore there only needs to be one instance of the object in memory at one
time. In this article, we’ll explore how to convert your objects into single
instance objects to improve performance and reduce resource usage within the .NET
development
framework.


Download version

A downloadable version of this article in PDF form,
including embedded code, is available in the
TechRepublic Download Center.


The problem with construction

The idea of a constructor for a class has been around since
the dawn of object-oriented
programming
. The object must have a chance to initialize itself before it’s
handed back to the calling program. This is great since it allows internal data
structures to be populated and configured.

The challenge is that the compiler or the runtime
environment has already allocated memory for the object. It has, in essence,
already created the new object and is just waiting to be configured.

This prevents the constructor from being used with a
singleton. The object has already been partially instantiated by the time the
code is called and because of this, we have to stop the developer from using
the constructor. The constructor must be made protected, private, or internal.
Once this is done, you’ll be able to call the constructor from within your
class, but the outside world won’t be able to call it directly.

By creating even one constructor, even if marked protected,
private, or internal, the default constructor isn’t created. This ensures that
no code, except the scope you’ve specified, can create the object.

The role of static methods

If the developer can’t use the constructor, she can’t create
her own object, which will make using the object very difficult. It’s hard to
use something that you can’t create, which is where static methods come in.
Since static methods operate directly off of the class, an instance isn’t
required. This allows the developer to call the methods before he has an
instance of the class.

The role of the static methods becomes the process of
looking up some internal cache to determine if the requested instance of the
object exists. If the instance exists, the calling program is provided a
reference to that instance. If the instance doesn’t exist, the static method
automatically creates it, stores it in the internal cache, and returns a
reference to the object.

The collection

We now know how to prevent a constructor from being created
and how to allow developers to create instances through static methods.
However, the missing piece is the cache which holds the instances of the class
that you want to save and offer up to future calls. That’s the role of the
collection. The key to the whole program is being able to look up and find
previous instances of the class that you’ve instantiated. Collections can store
objects and index them for the lookup the static method will need to do.

A collection is a necessary component of being able to cache
the objects, and therefore is a necessary part of creating a singleton class.

Putting it all together

The prerequisite pieces are in place for creating your own
singleton class. Listing A shows a Method class, and Listing B
shows the MethodCollection class.
These two classes cause a single instance of any given data to be created in
the Method class. This code, which
was extracted from a larger IIS Log Parsing application, has been designed to
take the relatively few request methods that are encountered in a log file and
prevent literally millions of instances from being created. The Pseudo code
comments in the listings are in place to remove unnecessary details, such as
database access.

The collection design has two basic retrieval methods. These
methods, both of which are GetMethod()
overrides, fetch a method by either its integer ID or the string
representation. At this level, a null is returned if the method isn’t found. On
that are layered two GetOrCreateMethod() overrides. These call GetMethod() but create a method if one
is not found. This allows the calling code to not worry whether the method
exists in the database or not—if not, it is automatically created.

Each of these functions has a corresponding static
counterpart. These static methods work on a single global list of methods. This
works by a static method which returns or creates a global instance of the Method collection. The static prefixed
methods use the global instance and call the methods described above.

The net effect is that the collection can be used directly.
This is in case there is some need to have a subset of the methods available,
but it also has a global instance, so it isn’t necessary for the developer to
maintain his own list if he doesn’t want to.

On the Method
class side, it also supports a set of GetMethod
static functions. These are the ones that the MethodCollection calls. These either return the method, or they
will return null. An oddity you may see in the Method class is that there’s an internal constructor which takes in
a data row. This is designed as a performance enhancer. It allows the object to
be instantiated with a pre-populated row of data. This isn’t a big enhancement
with such a small class, but in larger classes with a potentially large number
of instances, it allows the creation without another database query and does improve
performance.

The challenge of multithreading

For the scope of this singleton class, multithreading would
be a problem because it would be possible to create multiple instances of the
method name as objects—even in the database. In many cases creating a singleton
doesn’t require much thought towards multithreading; however, when it does,
you’ll need to add your own critical section handling to prevent two
simultaneous attempts from running the same addition code, which will prevent
the addition of duplicate records.


Additional white paper resources

“Tune
in to Interoperability Month”

“MSDN
Webcast: Web Services (Level 300)”

Application
Delivery: The Game is Changing”