DEV Community

Sungu, Hasan Emrah
Sungu, Hasan Emrah

Posted on

Singleton Pattern Implemented in C#

Singleton pattern is a very well known design pattern in software engineering. It is a very controversial topic and you can find many heated discussions about why Singleton pattern is the source of all the evil in the world or when it is acceptable to use it. However, if you are in game development like myself, you already know that it is a pretty commonly used design for Manager type classes such as CutSceneManager or InputManager and so on.

I will not go into details about what Singleton is or when to use it but to put it simply, a Singleton is a class which you expect to have only one instance of through out an application's life.

I have recently moved to a team whose members are mostly junior engineers as a senior engineer. I have been going through the source code to understand where I needed to apply some "fixing" before I actually started coding. I started with main game loop and went on to the initializations of the Manager type classes, which are expected to be Singleton. To my surprise all of them had public constructors and fixing these became my first task.

In its most simple every game looks like the following→

private readonly Managers[] _managers... 

static void main(string[] args){
    //Initialize XXXManager
    //Initialize YYYManager
    //Initialize ZZZManager

    while(true){
        foreach(var mgr in _managers){
            mgr.Update();
        }
        if(someCondition){
            break;
        }
    }

    //Reverse of creation order
    //Destroy ZZZManager
    //Destroy YYYManager
    //Destroy XXXManager
}

It is clear that each Manager class is a Singleton which implements an interface with methods Initialize, Update and Destroy. Moreover, it is a good idea to manage the creation of those Singleton instances so that Singletons are accessed through a single entry point (I will come to that in a bit). So here is the base class for each Singleton class to inherit from.

public abstract class SingletonBase<T> {

/// <summary>
/// Instance creator for this class. <see cref="Lazy{T}" /> guarantees
/// that <see cref="InstanceCreator" /> will only run once.
/// </summary>
private static readonly Lazy<T> LazyInstance = new Lazy<T>(InstanceCreator);

#if DEBUG

/// <summary>
/// Counter which holds how many instances this class has.
/// Since this is a Singleton implementation,
/// <see cref="_instanceCount" /> can at most be one (1).
/// </summary>
private static int _instanceCount;

#endif

 /// <summary>
 /// Every Singleton implementation is required to inherit this base class.
 /// </summary>
protected SingletonBase() {
#if DEBUG
    Interlocked.Increment(ref _instanceCount);
    if (_instanceCount > 1) {
        throw new Exception($"{nameof(T)} is Singleton but it has {_instanceCount} instances");
    }
#endif
}

/// <summary>
/// Access the instance of this Singleton. The first time you access
/// it is lazily instantiated.
/// </summary>
/// <remarks>This property must be thread-safe.</remarks>
public static T Instance => LazyInstance.Value;

/// <summary>
/// </summary>
/// <returns>An instance of <see cref="T" /></returns>
private static T InstanceCreator() {
#if DEBUG
    var ctors = typeof(T).GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
    var publicCtors = ctors.Where(c => c.IsPublic);
    if (publicCtors.Any()) {
        throw new HasPublicConstructorException(typeof(T));
    }

    var ctorsWithParameters = ctors.Where(c => c.GetParameters().Length > 0);
    if (ctorsWithParameters.Any()) {
        throw new HasConstructorWithParametersException(typeof(T));
    }
#endif
    //Since this will be run only once during the application's life cycle it is acceptable to use.
    //If it would be called many times, it is better to CreateDelegate
    return (T) Activator.CreateInstance(typeof(T), true);
}

I tried to comment as detailed as possible so the code itself is self explanatory and #if DEBUG preprocessor is there on the assumption that by the time the release phase is reached, all the tests are passed and extra checks are already unnecessary. It may look like a micro optimization but every little bit helps :)

Now that the base singleton is created, it is pretty straight forward to create a base GameManager class, which all the game managers need to inherit from. As can be seen, it only has the bare minimum with a really clean interface.


    public interface IGameManager {

        void Initialize();

        void Update();

        void Destroy();
    }

    public abstract class GamaManagerBase<T> : SingletonBase<T>, IGameManager  {


        protected GamaManagerBase(){

        }

        public abstract void Initialize();

        public abstract void Update();

        public abstract void Destroy();
}

Finally, I would like mention about the single entry point for the Singleton instances as I do not want the users of this instances to be accessed erratically. I will create a SingletonManager class, which will handle all the access

public class SingletonManager : SingletonBase<SingletonManager> {

    /// <summary>
    /// Cache which holds Type and its lazily instantiated object.
    /// </summary>
    private readonly Dictionary<Type, Lazy<object>> _cache = new Dictionary<Type, Lazy<object>>();

    /// <summary>
    /// Classes implementing <see cref="SingletonBase{T}"/> can be registered at <see cref="SingletonManager"/>.
    /// </summary>
    private SingletonManager() {
    }

    /// <summary>
    /// Registers the given type to <see cref="SingletonManager"/>.
    /// </summary>
    /// <typeparam name="T">Type to register to <see cref="SingletonManager"/></typeparam>
    /// <exception cref="AlreadyRegisteredTypeException">Throws if the type being registered is already registered.</exception>
    public void Register<T>() where T : SingletonBase<T> {
        if (_cache.ContainsKey(typeof(T))) {
            throw new AlreadyRegisteredTypeException(typeof(T));
        }
        _cache[typeof(T)] = new Lazy<object>(() => SingletonBase<T>.Instance);
    }

    /// <summary>
    /// Gets the Instance of given type.
    /// </summary>
    /// <typeparam name="T">Type to get.</typeparam>
    /// <returns>The instance of T, implementing <see cref="SingletonBase{T}"/></returns>
    /// <exception cref="NotRegisteredTypeException">Throws if the type being registered is not registered.</exception>

    public T Get<T>() where T : SingletonBase<T> {
        if (_cache.TryGetValue(typeof(T), out var value)) {
            return (T)value.Value;
        }
        throw new NotRegisteredTypeException(typeof(T));
    }
}

Now, here is the expected usage of SingletonManager class→

private static void Main(string[] args) {
    var manager = SingletonManager.Instance;
    manager.Register<CutSceneManager>();
    manager.Get<CutSceneManager>().Initialize();
    manager.Get<CutSceneManager>().Update();
}

TL;DR

How to implement singleton pattern in c# and a working example

Top comments (2)

Collapse
 
justinkaffenberger profile image
JustinKaffenberger • Edited

This is a good example of manually implementing the singleton pattern!

However...

I'd recommend using a dependency injection container to control the lifetime of certain instances. For example, in most DI containers you can simply register a type as singleton. When resolving an instance of that type, the DI container will ensure the same instance is returned for all requests. This gives you the behavior you're looking for without having to implement a base class.

Collapse
 
emrahsungu profile image
Sungu, Hasan Emrah • Edited

Thanks a lot for your comment and I agree that that DI containers are useful in many applications such as REST API and so on but they are pretty much not used in game development, at least according my experience about 10 years in this field. It is just seen as 'over-engineering'. Of course I repeat, I am not saying it is bad but the inclusion of an entire framework and methods just to achieve DI is little short of crazy :D, especially it makes developers destroy perfectly clean interfaces just to be able to inject some interchangeable component which will 99.99% of the time never be interchanged.

However, I see many arguments that even if the components will not be interchanged, one must be able to test them in isolation with mock objects and etc. However, I will never buy into the argument that it's worthwhile complicating an interface just to be able to better test it. While testing proves that your tests work, a non-bloated interface is the way forward proving that your code actually works. You would not believe the amount of tests I have seen, just written so that it just passes the test or no errors in unit tests and integration test but the application is full of bugs and crashes.

As a final note, this is just a trivial implementation, in C#, of what we are doing actually in the game and it has been optimized twice already to match our needs. Consider that having 25 manager type classes and each and every Update method will be called 60 times a second, you would not want a framework in your code base where you are not freely alter the logic, especially if you have no control over when to allocate memory