Singleton Pattern and AddSingleton of .net core - Part 1 (Singleton Design Pattern)

I will start with implementation of Singleton pattern and what needs to be take care and multiple approach around that. While working with .net core, its default IoC container also support to register dependency as Singleton. There are some similarity and there are some difference with pattern. Also there are couple of things needs to taken care otherwise it is possible that you have multiple object instance same type.

I have covered in total 3 parts.

Singleton Pattern

This belongs to creational pattern. This provide a way that only single instance of class present that implemented as a singleton.

Solution 1

Simplest way to implement or obtain the singleton is use static class. Beware that it comes with its disadvantage.

public static class SampleOne
{
    public static void PrintMessage(string message)
    {
        Console.WriteLine(message);
    }
}
  • Mainly use for container for Utility method.
  • Also it will be used for extensions method in C#.
  • The main disadvantage of static class is that it can not be pass as a parameter to any method as it is not instance at all.
  • This will reduce the testability of your code bit hard as you can not replace it with test double.

Solution 2

-For all other solutions we will create simple class and it will have one property that hold instance of that class. Mostly single instance of that class.

public class SampleOne
{
    public static readonly SampleOne Instance = new SampleOne();

    /// <summary>
    /// Private constructor.
    /// This is to avoid its access to 
    /// </summary>
    private SampleOne()
    {

    }
}

To check this

Console.WriteLine(SampleOne.Instance.GetHashCode()); 
Console.WriteLine(SampleOne.Instance.GetHashCode());

Above both statement will return same value. If you are aware that GetHashCode is unique for each object. This is so far so good. One disadvantage of this solution is that as soon as SampleOne is being referenced, its instance is created so it is not lazy loaded.

Solution 3

Here we extend the Solution 2 and try to make it lazy loaded or say that instance is only created when it is being used first time and for sub-sequent same instance is being return. This way it get delayed in constructing object and it is very useful for memory usage. Just think that there are two many different type of singleton in application and some of them might not in real use but get initialized.

public class SampleOne
{
    private static SampleOne? _instance = null;


    /// <summary>
    /// Private constructor.
    /// This is to avoid its access to 
    /// </summary>
    private SampleOne()
    {

    }

    public static SampleOne? Instance
    {
        get 
        { 
            if( _instance == null )
                _instance = new SampleOne();
            return _instance; 
        }
    }
}

Now you see when Instance property get accessed at that time it check that instance already created or not and based on that it create new instance or return already created instance.

So far so good and we have solved all the problem...really...

Just for a moment think of concurrency when two process try to access Instance at same time and that is very much possible in multi-threaded scenario. So Solution 3 is good but it is not thread-safe.

To check that, try following code. You will see that there are different hashcode and we are expecting single hashcode.

Parallel.ForEach(Enumerable.Range(1, 100), cc =>
{
    Console.WriteLine(SampleOne.Instance.GetHashCode());
});

Solution 4

Here again we extend the solution 3. Here we try to put some lock during object creation so by this way only one thread at a time access that portion of code and we can avoid concurrent access of that area. This is what need to solve problem.

public class SampleOne
{
    private static SampleOne? _instance = null;
    private static object _instanceLock = new object();

    /// <summary>
    /// Private constructor.
    /// This is to avoid its access to 
    /// </summary>
    private SampleOne()
    {

    }

    public static SampleOne? Instance
    {
        get 
        {
            lock (_instanceLock)
            {
                if (_instance == null)
                {
                    _instance = new SampleOne();
                }
            }
            return _instance; 
        }
    }
}

To check this we can use same code in Solution 3 to check the hashcode. This time for all 100 times it print single hashcode value. So this seems to be final solution. Only problem with this solution is that each try to lock for object creation portion for each time that property get accessed. This is not required if object already created. To make it more performant, It is good we place lock only when object is null and specially around object creation but again we have to check that object is null or not.

Solution 5

As explain at the end of solution 4. Following could be the solution.

public class SampleOne
{
    private static SampleOne? _instance = null;
    private static object _instanceLock = new object();

    /// <summary>
    /// Private constructor.
    /// This is to avoid its access to 
    /// </summary>
    private SampleOne()
    {

    }

    public static SampleOne? Instance
    {
        get 
        {

            if (_instance == null)
            {
                lock (_instanceLock)
                {
                    _instance = new SampleOne();
                }
            }

            return _instance; 
        }
    }
}

Now if you check, there are chanced that there are multiple different instance created. Why ? Because null checking is outside lock so it is possible that two thread check and object null and then one of them acquire lock and another wait so ultimately both created object.

Solution 6

To solve problem raised in Solution 5. We have to do double checking. One checking is without thread safe so if object is not null then no lock acquire and return instance that is already there so we get better performance ( Problem raised in solution 4) and one check inside lock for null so even if thread wait for lock to release and later it get lock then also it get reference so no second object created.

public class SampleOne
{
    private static SampleOne? _instance = null;
    private static object _instanceLock = new object();

    /// <summary>
    /// Private constructor.
    /// This is to avoid its access to 
    /// </summary>
    private SampleOne()
    {

    }

    public static SampleOne? Instance
    {
        get 
        {

            if (_instance == null)
            {
                lock (_instanceLock)
                {
                    if(_instance == null)
                    _instance = new SampleOne();
                }
            }

            return _instance; 
        }
    }
}

To Try this, test with the same code as present in Solution 4.

Hope this is helpful. Coming article we will look at around Singleton in context of .net core DI container.