Introduction

After a long while, I had to create a singleton service while I’m developing a feature for the ABP Framework. Then I decided to write an article about that: Why and how should we use the singleton pattern over a static class?

OK, I agree that creating singleton class or static class, doesn’t matter, is not a good practice. However, in practical, it can be unavoidable in some points. So, which one we should prefer? While the article title already answers this question, I will explain details of this decision in this article.

While this article uses C# as the programming language, the principles can be applied in any object oriented language.

You can get the source code from my GitHub samples repository.

Using Dependency Injection?

Then you are lucky and you are doing a good thing, you can use the singleton lifetime.

ASP.NET Core allows you to register a class with the singleton lifetime. Assuming that you’ve a MyCache class and you want to register it with the singleton lifetime. Write this inside the ConfigureServices method of your Startup class and that’s all:

services.AddSingleton<MyCache>();

You still need to care about multi-threading, you shouldn’t inject transient/scoped services into your singleton service (see my dependency injection best practices guide for more), but you don’t need to manually implement the singleton pattern. ASP.NET Core Dependency Injection system handles it. Whenever you need to the MyCache service, just inject it like any other service.

However, there can be some reasons to manually implement the singleton pattern even if you use the dependency injection:

  • ASP.NET Core Dependency Injection system doesn’t allow to use services until the service registration phase completes. If you need to use your service before or inside the ConfigureServices then you can not get benefit of the dependency injection.
  • You can’t inject a service from a static context where you don’t have access to the IServiceProvider. For example, dependency injection may not be usable in an extension method.

Singleton Pattern vs Static Class

There are two main reasons that you should prefer singleton pattern over a static class. I will introduce briefly, then I will explain them in the next sections by details.

Testability

Singletons are well testable while a static class may not;

  • If your class stores state (data), running multiple tests might effect each other, so writing test will be harder.
  • Static classes are hard or impossible to mock. So, if you are testing a class depends on the static class, mocking may not be an easy option.

Extensibility

  • It is not possible to inherit from a static class, while it is possible with singleton pattern if you want to allow it. So, anyone can inherit from a singleton class, override a method and replace the service.
  • It is not possible to write an extension method to a static class while it is possible for a singleton object.

The Solution Overview

I created a solution that implements two caching services, one with the singleton pattern, the other one is a static class. I also created unit tests for both of the services:

Image for post

You can get the source code from my GitHub samples repository. The rest of the article will be based on this solution.

Implementing a Singleton Service

Let’s see a simple caching class that is implemented via the singleton pattern:

public class SingletonCache
{
    public static SingletonCache Instance { get; protected set; } = new SingletonCache();
​
    private readonly IDictionary<string, object> _cacheDictionary;

    protected internal SingletonCache()
    {
        _cacheDictionary = new Dictionary<string, object>();
    }
​
    public virtual void Add(string key, object value)
    {
        lock (_cacheDictionary)
        {
            _cacheDictionary[key] = value;
        }
    }
​
    public virtual object GetOrNull(string key)
    {
        lock (_cacheDictionary)
        {
            if (_cacheDictionary.TryGetValue(key, out object value))
            {
                return value;
            }
​
            return null;
        }
    }
​
    public virtual object GetOrAdd(string key, Func<object> factory)
    {
        lock (_cacheDictionary)
        {
            var value = GetOrNull(key);
            if (value != null)
            {
                return value;
            }
​
            value = factory();
            Add(key, value);
​
            return value;
        }
    }
​
    public virtual void Clear()
    {
        lock (_cacheDictionary)
        {
            _cacheDictionary.Clear();
        }
    }
​
    public virtual bool Remove(string key)
    {
        lock (_cacheDictionary)
        {
            return _cacheDictionary.Remove(key);
        }
    }
​
    public virtual int GetCount()
    {
        lock (_cacheDictionary)
        {
            return _cacheDictionary.Count;
        }
    }
}
  • The static Instance property is the object that should be used by other classes, like SingletonCache.Instance.Add(...) to add a new item to the cache.
  • I marked the setter as protected set to make it settable/replaceable only by a derived class.
  • The _cacheDictionary is not static because the object (Instance) is already static.
  • I declared protected internal for the constructor because;
  • protected makes possible to inherit from this class.
  • internal makes possible to create an instance of this class from the same assembly or an allowed assembly. I allowed to the SingletonVsStatic.SingletonLib.Tests project, so it can create an instance to test it (I used InternalsVisibleTo attribute in the Properties/AssemblyInfo.cs of the SingletonVsStatic.SingletonLib project to make it possible).
  • I make all methods virtual, so a derived class can override.

#aspnetcore #csharp #singleton-pattern #design-patterns #best-practices #programming-c

Why You Should Prefer Singleton Pattern over a Static Class
1.95 GEEK