How to make Unity singleton base class to support generics?

In my project, I have a class structure as shown in the image.

The green classes are old codes, that runs very well. The classes in red boxes are newly added codes. There're no compiler errors, however when click play in Unity and runs into the new code, the three classes can't be initialized correctly.

And unity console gives warning that says "The class named 'DataMgrBase`2' is generic. Generic MonoBehaviours are not supported! UnityEngine.GameObject:AddComponent()" at this line: "instance = obj.AddComponent ();"

How can I solve this problem?

Following are some code for your reference, thanks!

Implementation of singleton base class:

using UnityEngine;
using System.Collections;

public class UnitySingletonPersistent<T> : MonoBehaviour where T : Component
{
    private static T instance;

    public static T Instance {
        get {
            if (instance == null) {
                instance = FindObjectOfType<T> ();
                if (instance == null) {
                    GameObject obj = new GameObject ();
                    obj.name = typeof(T).Name;
                    obj.hideFlags = HideFlags.DontSave;
                    instance = obj.AddComponent<T> ();
                }
            }
            return instance;
        }
    }

    public virtual void Awake ()
    {
        DontDestroyOnLoad (this.gameObject);
        if (instance == null) {
            instance = this as T;
        } else {
            Destroy (gameObject);
        }
    }
}

Implementation of DataMgrBase:

public class DataMgrBase<TKey, TValue>: UnitySingletonPersistent<DataMgrBase<TKey, TValue>> {

    protected Dictionary<TKey, TValue> dataDict;

    public override void Awake()
    {
        base.Awake();

        dataDict = new Dictionary<TKey, TValue>();
    }

    public TValue GetDataForKey(TKey key)
    {
        TValue data;
        if (dataDict.TryGetValue(key, out data))
        {
            return data;
        }
        else
        {
            data = LoadDataForKey(key);
            if (data != null)
            {
                dataDict.Add(key, data);
            }

            return data;
        }
    }

    virtual protected TValue LoadDataForKey(TKey key)
    {
        if (dataDict.ContainsKey(key))
        {
            return GetDataForKey(key);
        }
        else
        {
            return default(TValue);
        }
    }    
}

Answers


I've solved it by myself as following:

Change of the base class to get a new generic type(of the class that will derive from it, and pass this type to singleton base class)

public class DataMgrBase<TKey, TValue, TClass>: UnitySingletonPersistent<TClass> where TClass: Component

For all the other three classes that want to derive from it, change them as following form:

public class MobSettingDataMgr : DataMgrBase<int, MobSettingData, MobSettingDataMgr>

You want something like:

public abstract class UnitySingletonPersistent<T> : MonoBehaviour where T:UnitySingletonPersistent<T>
{
...
}

Then in your concrete class:

public class DataMgrBase<TKey, TValue> : UnitySingletonPersistent<DataMgrBase<TKey, TValue> >
{
...
}

This is somehow answer that is not solving your problem, but will explain the problem.

MonoBehaviour cannot be generic for at least two reason:

1. Imagine you want to add generic component in Inspector from Unity3D editor. Now engine needs to know exactly all types in this component, not only casue it is going to be compiled in this moment, but also cause you could have public fields with undeclered types. Try to assign your UnitySingletonPersistent directly in Inspector, and you will see it is imposible.

2. Using AddComponent<T> where T is generic looks like could work, but also in this engine you can make so called prefabs out of instantiated GameObjects, and if this GameObject contains generic component Unity3D engine would need to support some kind of baking types, and in practice this would lead to generating scripts, each with diffrent types, and making big mess inside project. I hope you follow me.

But why it works for the components you marked with green color? Simply cause Unity3D engine knows all types when adding this component to GameObject.

To support all this Unity Technologies would need to make core changes in Unity3D engine, how it works now. It would make Unity3D completly diffrent engine as it is now.

So to solve your problem, there is only one way: no adding in runtime generic components, and getting rid of DataMgrBase class. So you will need to implement DataMgrBase logic in each component.


Need Your Help

VSX: Programmatically disabling breakpoints at runtime

c# .net visual-studio-2008 debugging vsx

How can breakpoints be enabled or disabled at runtime? I'm writing a test workbench application that can run other .net code for the purposes of debugging plugins. The workbench app itself is not

Does AT command depend on device or its an independent API command?

c# bluetooth at-command modem

I found some code on net that allow me to read/send SMS from a Mobile via bluetooth connection. But using USB EDGE modem I can't either send or read SMS. I just can connect to that modem's port.