In this tutorial we will cover following topics
- Implementation of Singleton design pattern.
- Why Singleton class is a static class.
- Multi-threading safety in Singleton.
- Lazy loading and Eager loading.
- Difference b/w Static class and singleton class.
This is very
popular creational design patterns which restrict us to have an instance of a
class.
Note: A singleton class can't have a public
constructor and it has to be sealed. The entry point to get the singleton
instance would be a static method or a static property.
Singleton
design pattern is implemented when we need one object of a class is initiated and
that object is used across the application like the following image
In above diagram
we will see different objects trying to invoke an object instantiated as
singleton. This single instance of the object is responsible to invoke
underneath methods or events.
We will
understand this Singleton design pattern with an example
1. Go to visual studio and select a
console application and give name as “SingletonApplication”
2. Add a class to this application and
give this class name as “Singleton” and write the following code to this class
using System;
using
System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SingletonApplication
{
// Sealed ensures to restricted in the derived class
public class Singleton
{
private static int counter = 0;
public Singleton()
{
counter++;
Console.WriteLine("Counter Value " + counter.ToString());
}
/*
* Public method which can be invoked
through the singleton instance
*/
public void PrintDetails(string message)
{
Console.WriteLine(message);
}
}
}
|
This
in this we created a function to print and a static variable to check that how
many instance is creating or this class.
Now
call “PrintDetails” function from the “Programe.cs” class like following
using System;
using
System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SingletonApplication
{
class Program
{
static void Main(string[] args)
{
/*
* Assuming Singleton is created
from employee class
* we refer to the GetInstance
property from the Singleton class
*/
Singleton fromEmployee = new Singleton();
fromEmployee.PrintDetails("From Employee");
/*
* Assuming Singleton is created
from student class
* we refer to the GetInstance
property from the Singleton class
*/
Singleton fromStudent = new Singleton();
fromStudent.PrintDetails("From Student");
Console.ReadLine();
}
}
}
|
Now
when we run this application and see
Above image
we can see that its counter value is incrementing as much time we are calling
to this class it means it’s creating the instance 2 time for “singleton” class
which is not the rule of singleton design pattern.
Now make some
changes in “singleton.cs” class and” program.cs” class.
Write the
following code to Singleton.cs class
using System;
using
System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SingletonApplication
{
// Sealed ensures to restricted in the derived class
public sealed class Singleton
{
private static int counter = 0;
/*
* Private property initilized with
null
* ensures that only one instance of
the object is created
* based on the null condition
*/
private static Singleton instance = null;
/*
* public property is used to return
only one instance of the class
* leveraging on the private property
*/
public static Singleton GetInstance
{
get
{
if (instance == null)
instance = new Singleton();
return instance;
}
}
/*
* Private constructor ensures that
object is not
* instantiated other than with in
the class itself
*/
private Singleton()
{
counter++;
Console.WriteLine("Counter Value " + counter.ToString());
}
/*
* Public method which can be invoked
through the singleton instance
*/
public void PrintDetails(string message)
{
Console.WriteLine(message);
}
}
}
|
Program.cs
class
using System;
using
System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SingletonApplication
{
class Program
{
static void Main(string[] args)
{
/*
* Assuming Singleton is created
from employee class
* we refer to the GetInstance
property from the Singleton class
*/
Singleton fromEmployee = Singleton.GetInstance;
fromEmployee.PrintDetails("From Employee");
/*
* Assuming Singleton is created
from student class
* we refer to the GetInstance
property from the Singleton class
*/
Singleton fromStudent = Singleton.GetInstance;
fromStudent.PrintDetails("From Student");
Console.ReadLine();
}
}
}
|
Run your
application and see the output
Now we can
see that counter value is not incrementing as much we create the object for
that class and this example follow the rule of singleton design pattern.
Why
singleton class is a sealed class
This can be a
question arise in our mind that when we have private constructor in singleton
class then what is the need to make that class as Sealed class.
We will
understand this with an example
Lets create a
class name “DerivedSingleton” and Inherit the singleton class ,it will give
compile time error that Singleton is inaccessible due to its protection level, Because
singleton is private class.
So now move
this derived class “DerivedSingleton” into the singleton class like following
code
using System;
using
System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SingletonApplication
{
public class Singleton
{
private static int counter = 0;
private static object obj = new object();
/*
* Private constructor ensures that
object is not
* instantiated other than with in the
class itself
*/
private Singleton()
{
counter++;
Console.WriteLine("Counter Value " + counter.ToString());
}
private static Singleton instance = null;
/*
* public property is used to return
only one instance of the class
* leveraging on the private property
*/
public static Singleton GetInstance
{
get
{
if (instance == null)
instance = new Singleton();
return instance;
}
}
/*
* Public method which can be invoked
through the singleton instance
*/
public void PrintDetails(string message)
{
Console.WriteLine(message);
}
/*
* By removing sealed keyword we can
inherit the singleton and instantiate multiple objects
* This violates singleton design
principles.
*/
public class DerivedSingleton : Singleton
{
}
}
}
|
We have remove sealed keyword from the class that why we are able to inherit .
Write the
following code in program class
using System;
using
System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SingletonApplication
{
class Program
{
static void Main(string[] args)
{
/*
* Assuming Singleton is created
from student class
* we refer to the GetInstance
property from the Singleton class
*/
Singleton fromStudent = Singleton.GetInstance;
fromStudent.PrintDetails("From Student");
/*
* Assuming Singleton is created
from employee class
* we refer to the GetInstance
property from the Singleton class
*/
Singleton fromEmployee = Singleton.GetInstance;
fromEmployee.PrintDetails("From Employee");
Console.WriteLine("-------------------------------------");
/*
* Instantiating singleton from a
derived class. This violates singleton pattern principles.
*/
Singleton.DerivedSingleton derivedObj = new Singleton.DerivedSingleton();
derivedObj.PrintDetails("From
Derived");
Console.ReadLine();
}
}
}
|
Now run your
application and see the output, you will see that counter value is incremented,
that proving that we are able to create multiple instances of the singleton
using the nested derived class
This violates
the principle of singleton. Let’s go back to the Singleton and make the
class as sealed and remove the DeriviedSingleton class.
Multithread
Safety in Singleton
Lazy
Initialization : The lazy initialization of an object improves the performance
and avoids unnecessary computation till the point the object is accessed.
Further, it reduces the memory footprint during the startup of the program.
Reducing the memory print will help faster loading of the application.
Lazy
Initialization in our example : GetInstance Property is responsible for the Singleton
Instance creation. Singleton object is not instantiated until and unless GetInstance is
invoked. Hence, there is a delay in instance creation till the GetInstance is
accessed. This Delay in Instance creation is called Lazy Initialization.
Basically Lazy initialization work fine
in single credential environment. In other case suppose that if multithreading
are invoking same getinstance
property at same instance of time then their chance that it create multiple
instance for this property.
Lets understand this with an example
Go to the Program class and write the following
code
using System;
using
System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SingletonApplication
{
class Program
{
static void Main(string[] args)
{
System.Threading.Tasks.Parallel.Invoke(
() =>
PrintStudentdetails(),
() =>
PrintEmployeeDetails()
);
Console.ReadLine();
}
private static void PrintEmployeeDetails()
{
/*
* Assuming Singleton is created
from employee class
* we refer to the GetInstance
property from the Singleton class
*/
Singleton fromEmployee = Singleton.GetInstance;
fromEmployee.PrintDetails("From Employee");
}
private static void PrintStudentdetails()
{
/*
* Assuming Singleton
is created from student class
* we refer to the
GetInstance property from the Singleton class
*/
Singleton fromStudent = Singleton.GetInstance;
fromStudent.PrintDetails("From Student");
}
}
}
|
Here System.Threading.Tasks.Parallel.Invoke is a static method which is given by the Microsoft to invoke
multiple method parallel.
This method take Action which you can pass using the
Lambda expression like following
System.Threading.Tasks.Parallel.Invoke(
() =>
PrintStudentdetails(),
() => PrintEmployeeDetails()
);
|
Now run your application and you will see that its
calling the class 2 time to avoid this situation LOCK keyword is the best way
to controller from calling a class multiple time
So for change the Singleton class like following
using System;
using
System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SingletonApplication
{
// Sealed ensures to restricted in the derived class
public sealed class Singleton
{
private static int counter = 0;
private static object obj = new object();
/*
* Private property initilized with
null
* ensures that only one instance of
the object is created
* based on the null condition
*/
private static Singleton instance = null;
/*
* public property is used to return
only one instance of the class
* leveraging on the private property
*/
public static Singleton GetInstance
{
get
{
if (instance == null)
{
lock (obj)
{
if (instance == null)
instance = new Singleton();
}
}
return instance;
}
}
/*
* Private constructor ensures that
object is not
* instantiated other than with in
the class itself
*/
private Singleton()
{
counter++;
Console.WriteLine("Counter Value " + counter.ToString());
}
/*
* Public method which can be invoked
through the singleton instance
*/
public void PrintDetails(string message)
{
Console.WriteLine(message);
}
}
}
|
Now run your application you will see
that this issue is resolved.
How to implement a Thread Safe singleton
class : Locks are the best way to control
thread race condition and they help us to overcome the present situation.
Please refer to the Singleton.cs code for lock checks and double check locking.
For more details on double check locking please refer to the below article
https://en.wikipedia.org/wiki/Double-checked_locking
For more details on double check locking please refer to the below article
https://en.wikipedia.org/wiki/Double-checked_locking
Non-Lazy or Eager Loading : Eager loading is nothing but to
initialize the required object before it’s being accessed. Which means,
we instantiate the object and keep it ready and use it when we need it. This
type of initialization is used in lower memory footprints. Also, in eager
loading, the common language runtime takes care of the variable initialization
and its thread safety. Hence, we don’t need to write any explicit coding for
thread safety.
Non-Lazy or Eager in our application
Write the following code in singleton
class
using System;
using
System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SingletonApplication
{
// Sealed ensures to restricted in the derived class
public sealed class Singleton
{
private static int counter = 0;
/*
* Private property initilized with
null
* ensures that only one instance of
the object is created
* based on the null condition
*/
private static readonly Singleton instance = new Singleton();
/*
* public property is used to return
only one instance of the class
* leveraging on the private property
*/
public static Singleton GetInstance
{
get
{
return instance;
}
}
/*
* Private constructor ensures that
object is not
* instantiated other than with in
the class itself
*/
private Singleton()
{
counter++;
Console.WriteLine("Counter Value " + counter.ToString());
}
/*
* Public method which can be invoked
through the singleton instance
*/
public void PrintDetails(string message)
{
Console.WriteLine(message);
}
}
}
|
When you will run application you will
see following output
Modify
eager loading to Lazy initialization using Lazy keyword
Singleton with Lazy keyword (.NET 4.0) : Lazy keyword provides support for lazy initialization.
In order to make a property as lazy, we need to pass the type of object to the
lazy keyword which is being lazily initialized.
By default, Lazy objects are
thread-safe. In multi-threaded scenarios, the first thread which tries to
access the Value property of the lazy object will take care of thread safety
when multiple threads are trying to access the Get Instance at the same
time.
Therefore, it does not matter which thread initializes the object or if there are any thread race conditions that are trying to access this property.
By default, Lazy
Therefore, it does not matter which thread initializes the object or if there are any thread race conditions that are trying to access this property.
using System;
using
System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SingletonApplication
{
// Sealed ensures to restricted in the derived class
public sealed class Singleton
{
private static int counter = 0;
private static readonly Lazy<Singleton> instance =
new Lazy<Singleton>(() => new Singleton());
public static Singleton GetInstance
{
get
{
return instance.Value;
}
}
private Singleton()
{
counter++;
Console.WriteLine("Counter Value " + counter.ToString());
}
/*
* Public method which can be invoked
through the singleton instance
*/
public void PrintDetails(string message)
{
Console.WriteLine(message);
}
}
}
|
Difference
between Lazy Loading and Non-lazy or Eagar Loading
Lazy
Loading
1. Improve the performance
2. Avoid the unnecessary load till the
point object is accessed
3. Reduces the memory footprint on the
start-up.
4. Faster application load.
Non-Lazy
loading
1. Pre-Instantiation of the object
2. Commonly used in lower memory
footprints
When
we should Lazy initialization
Now assume a
situation that we need to assign some static property in the singleton class
and those property need to be retrieve from the database ,in those situation we
use last initialization as we do not need to retrieve these values till the
singleton object is initialize .
Differences between Singleton and static classes
1.
Static is a keyword and Singleton is a design pattern
2.
Static classes can contain only static members
3.
Singleton is an object creational pattern with one instance of
the class
4.
Singleton can implement interfaces, inherit from other classes
and it aligns with the OOPS concepts
5.
Singleton object can be passed as a reference
6.
Singleton supports object disposal
7.
Singleton object is stored on heap
8.
Singleton objects can be cloned