A Simple Singleton Service

A few months ago I posted a video about using Singletons in Unity. I got a bit of feedback, mostly negative constructive about how the service wasn’t very well conceived. I get that a lot while I learn, after all I tend to think you need to write things the wrong way sometimes to appreciate the correct way. Also I am a programmer so essentially 90% of what I write will be “wrong” depending on who you talk to. However in this instance everyone was correct so I went away and devised a better approach¬†… I hope. I’m using Singletons less now I have a much better decentralized messaging service which I would like to talk about at some point but for now I thought I’d share my simpler and move effective SingletonService.

The Code

Lets have a look at the whole class and then break down into more manageable chunks.

using UnityEngine;
using System;
using System.Collections.Generic;

public static class SingletonService {

    private static Dictionary<Type, object> _singletons = new Dictionary<Type, object>();

    //Adds a singleton to the manager
    public static void RegisterSingletonInstance(T instance) where T : class {
        if (!_singletons.ContainsKey(typeof(T))) {
            _singletons[typeof(T)] = instance;
        } else {
            throw new System.InvalidOperationException("A singleton of this type has already been registered.");
        }
    }

    //Removes a singleton type from the manager
    public static void UnregisterSingletonInstance(T instance) where T : class {
        if (!_singletons.Remove(typeof(T))) {
            throw new System.InvalidOperationException("You are trying to remove a singleton that does not exist!");
        }
    }

    //Attempts to get a singleton from the manager
    public static T GetSingleton() where T : class {
        Type requestedType = typeof(T);
        object o = null;
        if (!_singletons.TryGetValue(requestedType, out o)) {
            throw new System.InvalidOperationException("The type you are attempting to retreive has not been registered as a Singleton are you sure you are looking for the correct type?");
        }
        return (T)o;
    }

}

The code here is fairly simple. Requires no inheritance on the part of the object registering to be a singleton. Does not “look up” items at run time with GameObject.Find and generally just fits in with my coding style better, so I guess that is a good thing for me!

So how does it work?

private static Dictionary<Type, object> _singletons = new Dictionary<Type, object>();

The dictionary above holds two variables, a Type and the instance of that type. As the Singleton Pattern limits us to only one instance of a particular Type if we store this as the key we have an easy way to determine if we already have a Singleton in our scene.

This is what happens when we register our object to be accessed as a Singleton.

    //Adds a singleton to the manager
    public static void RegisterSingletonInstance(T instance) where T : class {
        if (!_singletons.ContainsKey(typeof(T))) {
            _singletons[typeof(T)] = instance;
        } else {
            throw new System.InvalidOperationException("A singleton of this type has already been registered.");
        }
    }

Here we check if the dictionary already contains a Type that we want to register. It shouldn’t otherwise this item wouldn’t be a Singleton, but if for some reason we made a mistake it will throw an error letting us know we are trying to register it twice.

Unregistering (I’m not sure that is a word) is the inverse action of adding an item to the list. Interestingly attempting to remove an item from a collection that does not exist will not cause any errors. It just won’t remove anything.

    //Removes a singleton type from the manager
    public static void UnregisterSingletonInstance(T instance) where T : class {
        if (!_singletons.Remove(typeof(T))) {
            throw new System.InvalidOperationException("You are trying to remove a singleton that does not exist!");
        }
    }

However if we are attempting to remove something that doesn’t exist we again throw an error to the user just so they can be aware.

Lastly we need to access Singletons, that’s the whole point of doing this! So we pass in the type of the Singleton we require at run-time. We query the dictionary’s keys. If we find the correct key the value is store and returned to the user.

    //Attempts to get a singleton from the manager
    public static T GetSingleton() where T : class {
        Type requestedType = typeof(T);
        object o = null;
        if (!_singletons.TryGetValue(requestedType, out o)) {
            throw new System.InvalidOperationException("The type you are attempting to retreive has not been registered as a Singleton are you sure you are looking for the correct type?");
        }
        return (T)o;
    }

If the key is not found we throw an error so the users know this item isn’t a Singleton.

This is more efficient than my previous attempt because of the fast look-up times of a Dictionary. Which if my reading serves me correctly is essentially O(1) for lookup purposes.

Usage

Using this code is also very simple. Much like my tutorial on delegates we can use the OnEnable and OnDisable methods to register and unregister as a Singleton. This has the added benefit of ensuring only active scripts can be accessed. So lets say we have a script that we want to be a singleton we simply add the following.

    void OnEnable() {
        SingletonService.RegisterSingletonInstance(this);
    }

    void OnDisable() {
        SingletonService.UnregisterSingletonInstance(this);
    }

To access the Singleton we use the following static call. Pretend the script we are accessing is called GameManager. We would just call the following.

    var manager = SingletonService.GetSingleton();

And there you have it a Singleton service that has less overhead that my previous implementation that is more robust thanks to generics and reddit.