2

I am wondering which is the right way to use singleton instance: when I create a singleton Class called "Manager" and it contains an int variable called "value" and I have a another class called "A"...

    //singleton class
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    public class Manager : MonoBehaviour {
        public static Manager Instance{ set; get;}
        public int value;
        private void Awake () {
            if (Instance == null) {
                Instance = this;
                DontDestroyOnLoad (this.gameObject);
            } else {
                Destroy (gameObject);
            }
        }
    }

So inside my A class I can make a instance of the singleton like this: // example pseudo code public class A {

        // First
        // using as a global variable
        Manager manager;

        //------------------------------------

        Public void tstOne(){
             // First
             // using that global variable
             manager.Instance.value = 0;

             // Second
             // or this way
             Manager.Instance.value = 0; 
        }
        Public void tstTwo(){
             // First
             // using that global variabl
             manager.Instance.value = 1;

             // Second
             // or this way
             Manager.Instance.value = 1; 
        }
    }

So my problem is - should I create A global instance and then use that instance like in first one or should I use the second example?

Are they both are same in terms of memory consumption and efficiency? Or is there another way to use singletons?

  • depends ... currently none of your code makes a lot of sense .... where is the implementation of `Manager`? – derHugo Jun 04 '19 at 05:39
  • 3
    Possible duplicate of [How to implement a singleton in C#?](https://stackoverflow.com/questions/246710/how-to-implement-a-singleton-in-c) – Patrick W Jun 04 '19 at 05:42
  • There are no stupid questions macnmunasinghe, hopefully we can help you clarify what you are asking, and help you find your way to the answer. You may find this guide helpful https://stackoverflow.com/help/how-to-ask – Tim Ogilvy Jun 04 '19 at 05:46
  • As a matter of fact singletons are a well known *anti-pattern*, consider using dependency injection instead. Please have a look at [this link](https://stackoverflow.com/questions/137975/what-is-so-bad-about-singletons) for example. – Samuel Vidal Jun 04 '19 at 05:46
  • 3
    @SamuelVidal it depends a lot on your needs. Often singletons are a good way for providing global access to a manager component. In my personal opinion dependency injection 1. "hides" the dependency kind of the same way as a singleton pattern does and 2. itself actually kind of works like one big singleton for providing the desired references ... If you really want to be clean than don't have neither singletons nor dependency injection but rather manage your instance references "better" (whatever this means) e.g. by referencing stuff in the Inspector right from the beginning. – derHugo Jun 04 '19 at 05:53
  • @derHugo I beg to disagree ;-) – Samuel Vidal Jun 04 '19 at 05:55
  • @SamuelVidal lol you may ;) That's why I say in my opinion .. I know there is a lot of discussions about singletons vs dependency injection .. especially in Unity circles ... maybe I just don't like DI for no reason ;) – derHugo Jun 04 '19 at 05:57
  • hi @derHugo, Manger is the singleton class – macnmunasinghe Jun 04 '19 at 06:03
  • @macnmunasinghe please add a [Minimal, Complete and Reproducible Example](https://stackoverflow.com/help/minimal-reproducible-example) ... it's hard to tell where and how you are calling your code – derHugo Jun 04 '19 at 06:07
  • i edited the question hope it's more clear to understand now – macnmunasinghe Jun 04 '19 at 06:17
  • your setter should be private as in `public static Manager Instance{ get; private set;}`. Also the `Manager manager;` still makes no sense .... where/when will this reference be set? Also you would then simply do `manager.value = 0;` instead since you already have the reference to the instance. So `Manager.Instance` is the only `public static` reference that makes sense to be used .. otherwise what would you need the singleton pattern for? – derHugo Jun 04 '19 at 07:01
  • @derHugo The Unity editor is just a gigantic, complicated dependency injection framework :) – Colin Young Jun 04 '19 at 12:55

3 Answers3

1

Instance is a static field so you should get your references to the singleton with Manager.Instance.

Manager.Instance is a globally accessible instance because it's public static. There's no such thing as a global variable in C#.

Accessing a static field directly by the class name is definitely better than making a second instance as a field in A and then accessing the static instance using it.

Ruzihm
  • 19,749
  • 5
  • 36
  • 48
  • ok, @Ruzihm are you saying directly using Manager.Instance. is the correct way to use instead of declaring a global instance.Will there be any further explanation to your answer – macnmunasinghe Jun 04 '19 at 06:53
  • @macnmunasinghe `Manager.Instance` **is** a globally accessible instance because it's `public static`, so I'm not sure what to make of your question. There's no such thing as a global variable in C#. Anyway, accessing a static field directly by the class name is definitely better than making a second instance as a field in `A` and then accessing the static instance using it. – Ruzihm Jun 04 '19 at 06:58
  • yeah , that make this more understandable this should be the anwser – macnmunasinghe Jun 04 '19 at 07:09
1

I confirm Ruzihm answer (and I also agree with derHugo comment, I personally prefer SO and reference them where required. Much cleaner than Singleton or DI direct approach).

To be specific accessing the instance member manager.Instance.value is slower and also requires further memory allocation on the heap so it hurts both memory usage and speed performance. (Very small performance issues, but still.)

There's also further room of improvement on the singleton, especially if you do not require it to derive from MonoBehaviour.

You can:

  1. Make sure it is build also if you forget to add it in the scene
  2. Make it thread safe (not required by unity, usually)
  3. Implement it lazily (means you create it only when you need it)
  4. Also, as a general rule is better to seal the singleton class for improved performance (again really slight improvement when you use virtual methods)

This would be the implementation (considering you use the Manager as helper instance and YourManagerMono if you require monobehaviour logic:

public class YourManagerMono : MonoBehaviour
{
}

public sealed class Manager
{
    private static readonly Manager instance = new Manager();
    //use a gameobject to relate it in the scene
    private YourManagerMono monoManager;

    public static Manager Instance => instance;

    // Explicit static constructor to tell C# compiler
    // not to mark type as beforefieldinit
    static Manager() {}

    private Manager()
    {
        //find or create a manager
        monoManager = GameObject.FindWithTag("Manager").GetComponent<YourManagerMono>();
        if (monoManager == null)
        {
            monoManager = new GameObject().AddComponent<YourManagerMono>();
            monoManager.gameObject.tag = "Manager";
            Object.DontDestroyOnLoad(monoManager.gameObject);
        }
    }
}

A great article about singleton implementation for C# by Jon Skeet (I used implementation 4).

EDIT:
Again I agree with derHugo (in the new comment on this answer). My example is used to show an interesting prospective and to offer as much performance as I can, but if you just require a Monobehaviour with just point 1 and 3 you may go ahead with a generic implementation of singleton from Unify Community (just remember to set the end class as sealed to help the compiler).

Jack Mariani
  • 2,270
  • 1
  • 15
  • 30
  • Not much related to the question, so I add it as a comment. [This video](https://www.youtube.com/watch?v=raQ3iHhE_Kk) shows how to use SO for a cleaner implementation. – Jack Mariani Jun 04 '19 at 07:57
  • this is great!!! – macnmunasinghe Jun 04 '19 at 08:25
  • 1
    splitting this into a second class instead of directly doing the same within the `MonoBehaviour` is a question of taste I guess ... I wouldn't do it this way. Also the "lazy" part ... in most cases you don't want a game lag during the gameplay .. so it is probably better to instantiate/add etc the component right from the start of the App where a lag is still acceptable. – derHugo Jun 04 '19 at 08:42
  • Having 2 classes is not strictly required (but I see it as an interesting new option). The lazy implementation will most likely run at startup (because some script will ask forr it) so not a big deal. But I especially like the safecheck that creates the singleton if it's missing in the scene. In general, if you want just the monobehaviour with a safety check (safety lazy implementation) and not the second class I think the Unify solution works great. – Jack Mariani Jun 04 '19 at 09:13
1

For Singletons that you want to exist in one scene only, I use:

public class SceneSingleton<T> : MonoBehaviour
    where T : SceneSingleton<T>
{
    static T s_instance = null;

    public static T Instance
    {
        get
        {
            if (s_instance == null)
            {
                s_instance = Object.FindObjectOfType<T>();
            }

            return s_instance;
        }
    }
}

And then inherit your class from it, like this:

public class MyManager : SceneSingleton<MyManager>

If you need your singleton to stay alive between all scenes, try to use this:

public class GlobalSingleton<T> : MonoBehaviour
    where T : GlobalSingleton<T>
{
    static T s_instance = null;

    public static T Instance
    {
        get
        {
            if (s_instance == null)
            {
                GameObject prefab = Resources.Load(typeof(T).Name) as GameObject;

                if (prefab == null || prefab.GetComponent<T>() == null)
                {
                    Debug.LogError("Prefab for game manager " + typeof(T).Name + " is not found");
                }
                else
                {
                    GameObject gameManagers = GameObject.Find("GameManagers");
                    if (gameManagers == null)
                    {
                        gameManagers = new GameObject("GameManagers");
                        DontDestroyOnLoad(gameManagers);
                    }

                    GameObject gameObject = Instantiate(prefab);
                    gameObject.transform.parent = gameManagers.transform;

                    s_instance = gameObject.GetComponent<T>();
                    s_instance.Init();
                }
            }

            return s_instance;
        }
    }

   protected virtual void Init()
   { }
}
Dest
  • 459
  • 2
  • 7