Singleton Pattern - C#
Singleton Pattern in C#
What:
- Singleton pattern is part of Gang Of Four Creational Pattern.
- Ensures that a class has only one instance.
- Must have global access point to create the instance.
- Singleton should control across access to shared resource.
Where:
- Singleton pattern is used for Logging, Driver Objects, Caching, Thread Pool to name a few.
- Database Connection.
- Printer Spooling.
How:
- Singleton pattern does not allow any parameters to be specified when creating the instance.
- There are different ways to implement Singleton Pattern in C#
- Simple
- Thread-Safe
- Lazy Loading
- High Performance
- Though there are different ways to implement Singleton Pattern, they have the same characteristics:
- Single Constructor- Parameterless
- Single Constructor - Private
- Sealed class
- Static Variable references the single created instance.
- Public static access to the single created instance.
Example:
The easy. way to explain Singleton is by the concept of Logging. As we know that every software small or big does some kind of logging. This logging could be informational, to check for errors and warnings in the application.
Whenever the program has an error - we log exceptions based in the catch block say.
This logging functionality should and must have only one instance in the entire application.
Approach 1 : Creating a method name
public sealed class Logger
{
private static Logger _logger;
private Logger()
{}
public static Logger GetInstance()
{
if(_logger==null)
{
_logger= new Logger();
}
return _logger;
}
}
The above code has a constructor which is private. - This will eliminate the creation
of objects.
When you create an instance of a class, first thing that is called is a constructor.
- Simple
- Thread-Safe
- Lazy Loading
- High Performance
- Single Constructor- Parameterless
- Single Constructor - Private
- Sealed class
- Static Variable references the single created instance.
- Public static access to the single created instance.
Approach 2: Via properties
public sealed class Logger
{
private static Logger _logger;
private Logger()
{
}
public static Logger Instance
{
get
{
if(_logger==null)
{
_logger = new Logger();
}
return _logger;
}
}
}
The above code uses Properties to create an instance. This Property has only a getter.
You can call Logger obj= Logger.Instance to get an Object.
The above two approaches are NOT Thread-Safe approaches.
Two different threads could have evaluated the value of if(_logger==null) to true and could create two instances, which violates the Singleton Pattern.
Approach 3 : Lock ( Thread-Safe)
public sealed class Logger
{
private static Logger _logger;
private static readonly object Obj = new object();
private Logger()
{
}
public static Logger Instance
{
get
{
lock (Obj) //Here we are introducing lock.
{
if (_logger == null)
{
_logger = new Logger();
}
return _logger;
}
}
}
}
The above implementation is thread-safe. If you look at the code, we have introduce lock(Obj), which creates a lock and ensures that only one thread will create an instance. The disadvantage of the above approach is the performance of the application will reduce as a lock is acquired everytime an instance is requested.
Approach 4: Static Constructor.
public sealed class Logger
{
private static readonly Logger _logger = new Logger();
static Logger()
{
}
public static Logger Instance
{
get
{
return _logger;
}
}
}
The above approach uses Static Constructor which are specified to execute only when an instance of the class is created or a static member is referenced. The static constructors also are specified to execute only once per AppDomain.
Approach 5: Using Lazy
public sealed class Logger
{
private static readonly Lazy
lazyInstance = new Lazy(() => new Logger());
lazyInstance = new Lazy
public static Logger Instance
{
get
{
return lazyInstance.Value;
}
}
private Logger()
{
}
}
The above approach uses Lazy Keyword provided by .NET.
We can use Lazy Initialization to defer the creation of a large or resource-intensive object or the execution of a resource-intensive task. By Default, all public and protected members of the Lazy Generic class are thread-safe and may be used concurrently from multiple threads.
Disadvantages of Singleton Pattern:
- Since you cannot separate instances, unit testing becomes difficult.
- "S" in SOLID principle states "Single-Responsibility". You cannot completely isolate classes dependent on Singleton.
Comments
Post a Comment