Contents
Introduction
In this last article on learning singleton pattern we discussed what is singleton design pattern, when it is needed and what problems it solves. We also discussed how to create a basic singleton class step by step and how to enhance that class to be thread safe with the help of locking and performance effective with the help of double check locking. In this article, we’ll discuss Lazy initialization, the lazy keyword, why you make singleton class a sealed class and what are the differences between singleton and static class. Before we start, I strongly recommend going through my last article.
Lazy Initialization
In the last article, we discussed lazy initialization. Lazy initialization basically is very performance effective. In the situations like on-demand object creation where we create an object of the class only when it is accessed, lazy initialization works very well. It helps applications load faster because it does not depend upon instance creation at the time of application startup. There could also be situations where one would go for eager initialization. Eager initialization is the opposite of lazy initialization (or one could say non-lazy initialization). Let’s see how we can make our singleton class support eager initialization. You can get the source code we developed in our last article and get started. The same is attached along with the article as well.
Eager Initialization Implementation
Eager loading is basically initializing the object that is to be accessed in the future and keep it in memory rather than initializing it on demand so that we can use the object whenever we need those.
Open Visual Studio, the last solution of Singleton and open the Singleton
class.
- Change null backing field initialization to new
Singleton
class initialization as the following and make the backing field readonly.
- Since we made the backing field readonly, we cannot instantiate that again in our
SingleInstance
property, so just return the backing field as it is and remove the double check locking we implemented in our last version of singleton.
- Remove the locking variable as well that we declared earlier. Now run the application and check the output. Note that we still have two methods invoked parallelly.
We see that only one instance got created. This is not surprising that our class is still thread safe after removing lock variables because the CLR internally takes care of variable initialization and thus saves us from getting into a deadlock situation in eager loading initialization.
The following is the complete code.
using static System.Console;
namespace Singleton
{
class class Singleton
{
static int instanceCounter = 0;
private static readonly Singleton singleInstance = new Singleton();
private Singleton()
{
instanceCounter++;
WriteLine("Instances created " + instanceCounter );
}
public static Singleton SingleInstance
{
get
{
return singleInstance;
}
}
public void LogMessage(string message)
{
WriteLine("Message " + message);
}
}
}
Lazy Initialization Implementation using Lazy Keyword
We can modify our class to lazily initialize using a lazy keyword. Let’s check how we can do it.
- First change the backing field initialization to Lazy initialization via the following code
private static readonly Lazy<Singleton> singleInstance = new Lazy<Singleton>(()=>new Singleton());
This is the way we lazily initialize an object by passing the delegate to create instance as () => new Singleton()
- Now in the property the instance could not be returned directly, but we return
singleInstance.Value
because now singleInstance.Value
is Singleton class type and not the instance.
The full class code is as follows.
using System;
using static System.Console;
namespace Singleton
{
sealed class Singleton
{
static int instanceCounter = 0;
private static readonly Lazy<Singleton> singleInstance = new Lazy<Singleton>(()=>new Singleton());
private Singleton()
{
instanceCounter++;
WriteLine("Instances created " + instanceCounter );
}
public static Singleton SingleInstance
{
get
{
return singleInstance.Value;
}
}
public void LogMessage(string message)
{
WriteLine("Message " + message);
}
}
}
- Run the application and check the output.
We get the same output because lazy keyword created only one singleton class object and they are by default thread safe. That’s why we do not get any errors while invoking the methods accessing single instance parallelly.
Significance of Sealed Keyword in Singleton class
In the beginning of my last article on Singleton, I mentioned that we should use sealed keyword before our singleton class. This was because we do not want our singleton class to be inherited. Sealed class helps us to restrict class inheritance, but in singleton class it does more than that because inheritance is anyways restricted in singleton class whether we use sealed keyword or not in the class because all the constructors of singleton class are private, which will never allow singleton class to get inherited. For example, create a derived class and try to make your singleton a base class. Make the singleton as a public public class Singleton and add a new class in Visual Studio and call it DerivedClass
, now make Singleton
class as base of this newly added class. We immediately see an error as following:
It says that Singleton
class is inaccessible due to its protection level. So why do we actually we need sealed keyword when our purpose is already solved? We need sealed keyword in case of nested classes. For example, consider a scenario where this derived class is the nested class of the singleton class and also inherits from the singleton class. As per OOP it is perfectly possible. So our implementation becomes as follows:
using System;
using static System.Console;
namespace Singleton
{
public class Singleton
{
static int instanceCounter = 0;
private static readonly Lazy<Singleton> singleInstance = new Lazy<Singleton>(()=>new Singleton());
private Singleton()
{
instanceCounter++;
WriteLine("Instances created " + instanceCounter );
}
public static Singleton SingleInstance
{
get
{
return singleInstance.Value;
}
}
public void LogMessage(string message)
{
WriteLine("Message " + message);
}
public class DerivedClass : Singleton
{
}
}
}
Now try to access this derived class from the Main
method of Program.cs.
Since DerivedClass
is a nested class we can call it via Singleton
class and since it inherits from Singleton
class it also has access to the LogMessage()
method. Now run the application and check the output.
We clearly see that two instances of the Singleton
class are created when we run the application. The first instance was created by the parallelInvoke’s call to Log methods and the second instance is created by the Derived
class constructor as it implicitly called the base class constructor when the derived class instance was created. So in any case, whether it is a nested class or non-nested class, we have to restrict inheritance in singleton class. Since private constructors do not help us in restricting inheritance in case of nested classes, we should use sealed keyword to restrict inheritance. Now apply sealed keyword on Singleton
class and check its nested derived class.
Derived class:
Now when we have applied sealed on base singleton class, derived class says that it cannot inherit from the sealed class, no matter if the derived is nested or not. So, we would never have two instances of singleton class as it would never be derived.
using System;
using static System.Console;
namespace Singleton
{
public sealed class Singleton
{
static int instanceCounter = 0;
private static readonly Lazy<Singleton> singleInstance = new Lazy<Singleton>(()=>new Singleton());
private Singleton()
{
instanceCounter++;
WriteLine("Instances created " + instanceCounter );
}
public static Singleton SingleInstance
{
get
{
return singleInstance.Value;
}
}
public void LogMessage(string message)
{
WriteLine("Message " + message);
}
}
}
Singleton vs Static class
We’ll not discuss in detail what static class is, but rather focus more on the differences between singleton and static class. The following are point to point differences between a static class and singleton pattern.
- Singleton is an object creational design pattern and is designed following some guidelines to ensure it returns only one object of its class whereas static is a reserved keyword in C# which is used before class or method to make it static.
- Singleton classes or design pattern can contain static as well as non-static members where as if a class is marked static, it only should contain static members. For example, if a class is static, it should have static methods, static properties and static variables only.
- Singleton methods could be passed as reference to other methods or objects as a parameter, but static members could not be passed as a reference.
- Singleton objects could be created in a way where they support disposal; that means they could be disposed.
- Singleton objects are stored on heap whereas static objects are stored on stack.
- Singleton objects can be cloned.
- Singleton promotes code re-usability and code-sharing and could implement interfaces as well. Singleton can inherit from other classes and promotes inheritance and have better control on object state where as static class cannot inherit its instance members.
- Singleton class could be designed in a way where it can leverage lazy initialization or asynchronous initialization and is taken into consideration directly by CLR whereas static class is firstly initialized at the time of its first load.
- Static class cannot contain instance constructors and so cannot be instantiated whereas singleton classes have private instance constructor.
Static Class Implementation
Let’s do some practical implementations to understand in more detail. Open the solution we created or a new one and add a new project of console application type and name it as StaticClasses
.
Now suppose we have a requirement in our project to create a utility class that holds the area of various shapes. For example, let’s consider that the class contains the formulas to calculate area of circle, square, rectangle and triangle. We are sure that the formula to calculate area of the geometrical shapes always remain same and is based on what input parameters we pass. For example, the area of rectangle is the length of the rectangle multiplied by width of the rectangle and that of square is square of the length of the side. Since the area always remains the same we can achieve this functionality with the help of static class and static methods. So, create a class named Areas and make it static in the StaticClasses
console application.
Now let’s create static methods for calculating area of circle, rectangle, square and triangle.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static System.Math;
namespace StaticClasses
{
public static class Areas
{
public static double AreaOfCircle(double radius)
{
return PI * (radius * radius);
}
public static double AreaOfSquare(double side)
{
return side * side;
}
public static double AreaOfRectangle(double length, double width)
{
return length * width;
}
public static double AreaOfTraingle(double baseOfTraingle, double heightOfTraingle)
{
return (baseOfTraingle * heightOfTraingle)/2;
}
}
}
Since our class is static, it should only contain static methods, variables and properties and so we defined the static methods to calculate area of Rectange, Square, Circle and Triangle, which takes input parameters. And since formula always remains same, then do some computation as per formula and return the result to the caller. Note that I have used PI from static Math class, since it is a static property and the value of pi always remains same, we can use it as is to calculate the area of circle. Note that I have defined static class in the usings, this is the new way in which we can define static classes in usings and then we can use their members directly in the class without the static class name. I did the same with the System.Console
class in the main method.
In the main method we now call the methods by passing parameters to the methods in the following way.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static System.Console;
namespace StaticClasses
{
class Program
{
static void Main(string[] args)
{
double radiusOfCircle = 10.8;
double lengthOfRectangle = 5.5;
double widthOfRectangle = 2.3;
double sideOfSquare = 4.0;
double heightOfTriangle = 9.0;
double baseOfTriangle = 5.0;
WriteLine("Area of Rectangle with length {0} and width {1} is {2}", lengthOfRectangle, widthOfRectangle, Areas.AreaOfRectangle(lengthOfRectangle, widthOfRectangle));
WriteLine("Area of Square with side {0} is {1}", sideOfSquare, Areas.AreaOfSquare(sideOfSquare));
WriteLine("Area of Circle with radius {0} is {1}", radiusOfCircle, Areas.AreaOfCircle(radiusOfCircle));
WriteLine("Area of Triangle with height {0} and base {1} is {2}", heightOfTriangle, baseOfTriangle, Areas.AreaOfTraingle(heightOfTriangle, baseOfTriangle));
ReadLine();
}
}
}
In the above mentioned main method, we call the static methods by directly referencing through class name Areas. Note that we do not have to create an object first to call the static methods and they could directly be called via class name. When we run the application, we get the following output:
So, we can see that we can use static class and static methods working as a helper class to do straightforward tasks and get the result.
The choice between Static and Singleton
Now we know how to implement a static class and how to implement singleton design pattern and make the class singleton. The question is then, when to use static and when to use the singleton. We can say that singleton gives us more flexibility over the design and more control over the design which we can change as per the demand and need of the requirement. So, the use of static or singleton is only as per need looking at their pros and cons. In real-world scenarios, singleton could be used in logging, handling database connections, print spooling etc. Just keep in mind that singleton is an architectural concept whereas static is something pre-available from the .NET framework. There are potential risks in using static in the cases where we need object disposal as static members do not get disposed until the application is ended and always remain in memory. Moreover, the static variables are global in a way where they are shared between the different application and using it in web applications or ASP.NET applications could be risky as the variable would be shared to all the users regardless of their sessions because these variables will stay in an application domain. We have discussed a lot about static and singleton to make a choice while development. I hope these pointers help you to decide when to use what.
Conclusion
In this article, we learned what is lazy initialization and what is eager initializations with practical examples. We also learned why it is necessary to make the singleton class sealed with the sealed keyword. We learned the differences between static and singleton class and where to use static and where to use singleton classes.
<< Previous Article