This article introduces some basic object oriented concepts and explains the various strategies in C# .Net to design Singleton pattern.
What
A Class that can be instantiated only once is called as Singleton class. This singleton class does not allow client applications to create object instance, instead it expects the class to create the instance by itself and return the instance upon client's request.
How
Let's learn some basics of the object oriented programming (in C#). Most of the readers are familiar with OO Programming in C#. But to the benefit of other readers, I would like to discuss some key information about the constructor and access modifiers.
Constructors are used in object creation. Constructors are used for initializing the members of a class whenever an object is created. Constructors are similar to member methods.
Let's see how object is created from the client application from the below code.
Object obj = new Object();
The above code snippet can be split in to two parts.
-
Type definition
-
Object construction
Left-hand side of the expression defines the type of the object and right-hand side constructs the object by calling the specified constructor. If there are no default (parameter less) constructor, CLR creates one and the client will create the object as "new Object();". If the object needs to be constructed using the constructor with parameters, then parameters needs to be passes during object construction. For ex., If the class "Object" has a constructor with parameter that takes 2 integer parameters then the syntax would be "new Object(a, b);"
Constructor is also a class member and will have access modifiers like any other members. Access modifiers control the object construction.
The following are the access modifiers for constructors,
Public : A constructor that is defined as public will be called whenever a class is instantiated.
Protected : A constructor is defined as protected in such cases where the base class will initialize on its own whenever derived types of it are created.
Private : A constructor is defined as private in such cases whenever a class which contains only static members has to be accessed will avoid the creation of the object for the class.
Internal : An internal constructor can be used to limit concrete implementations of the abstract class to the assembly defining the class. A class containing an internal constructor cannot be instantiated outside of the assembly.
External : When a constructor is declared using an extern modifier, the constructor is said to be an external constructor.
Constructors can be classified into 2 types. They are defined below
Static : It is used for initializing only the static members of the class. These will be invoked for the very first time the class is being loaded on the memory. They cannot accept any arguments. Static Constructors cannot have any access modifiers.
Non-Static : are used for initializing the Non-Static and Static members of a class. These will be invoked every time a new object is defined for a class.
From the basics we learnt above let us build a Singleton Class using different strategies.
Strategy 1 - Lazy Instantiation
-
Constructor is defined as private. So there is no scope for the client to instantiate the object for the class "Singleton".
-
Declares a private member to hold the object instance of the Singleton class.
-
GetInstance() method is exposed as static method with in the public scope. This method returns the object handle for the "Singleton" class after instantiation. Since the object for the Singleton class cannot be created from the client, it is declared as "static". So, the client application would call this method as follows to get the object handle.
Singleton objSingleton = Singleton.GetInstance();
-
This GetInstance() method creates object instance only when the instance is null or not created. Private member declared in this class is declared as "static" since the GetInstance() method is static. Static members cannot access non-static members.
Code Snippet:
public class Singleton
{
private static Singleton objInstance;
private Singleton()
{
}
public static Singleton GetInstance()
{
if (objInstance == null)
{
objInstance = new Singleton();
}
return objInstance;
}
}
The above approach is called "Lazy Instantiation" because the object is not instantiated till it is requested for.
Note: Name Singleton in the class definition is not a keyword. It is a user-defined name to the class.
Above code snippet is not thread-safe. When there is concurrent requests are made to the GetInstance() method, both the requests will try to create the object instance initially as the instance would be a null reference. This could result in multiple instances of Singleton class. To overcome this, we should lock the object creation for concurrent access. Modified code snippet is given below.
public class Singleton
{
private static Singleton objInstance;
private static Object obj = new Object();
private Singleton()
{
}
public static Singleton GetInstance()
{
lock(obj)
{
if (objInstance == null)
{
objInstance = new Singleton();
}
}
return objInstance;
}
}
Strategy 2 - Static Initializer
Object is initialized before it is requested. If you closely monitor the below code snippet, GetInstance() method does not create the instance. Instead, it returns the instance that is created part of the initialization.
Code Snippet:
public class Singleton
{
private static Singleton objInstance = new Singleton();
private Singleton()
{
}
public static Singleton GetInstance()
{
return objInstance;
}
}
Strategy 3 - Static Classes
This was introduced only in .NET Framework 2.0.
A class can be declared static to indicate the framework that all members of the class are static. It is not possible to create the object instance. Advantage of static class is, they are loaded when the namespace containing this static class are loaded. They are sealed and cannot create subclasses out of it.
public static class Singleton
{
private static Singleton objInstance;
private static Singleton()
{
if (objInstance == null)
{
objInstance = new Singleton();
}
}
}
In the above code snippet, if you notice even the constructors are defined as static and they are used to assign values to static members. Static constructors has performance bottleneck and impose a substantial overhead.
Static classes are otherwise known as type initializers.
When
Main usage of the singleton pattern is to store and manipulate global data from one location.
Conclusion
In all the above mentioned approaches, the object instance is declared as static in the Singleton class. Static objects or members are stored in stack and they exist till life time of the appdomain they got loaded in.