如何让 Unity singleton base class 支持泛型?

How to make Unity singleton base class to support generics?

在我的项目中,我有一个 class 结构,如图所示。

绿色classes是旧代码,运行良好。红框里的classes是新加的代码。没有编译错误,但是在Unity中点击播放运行到新代码时,三个class无法正确初始化。

Unity 控制台在这一行发出警告,指出 "The class named 'DataMgrBase`2' is generic. Generic MonoBehaviours are not supported! UnityEngine.GameObject:AddComponent()":"instance = obj.AddComponent ();"

我该如何解决这个问题?

下面是一些代码供大家参考,谢谢!

单例基础的实现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);
        }
    }
}

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);
        }
    }    
}

你想要这样的东西:

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

然后在你的具体class:

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

这个答案并不能解决您的问题,但可以解释问题。

MonoBehaviour 至少有两个原因不能通用:

1. 假设您想从 Unity3D 编辑器在 Inspector 中添加通用组件。现在引擎需要确切地知道这个组件中的所有类型,这不仅是因为它会在这一刻被编译,而且还因为你可能有 public 个具有未删除类型的字段。尝试直接在 Inspector 中分​​配你的 UnitySingletonPersistent,你会发现这是不可能的。

2. 使用 AddComponent<T> 其中 T 是通用的看起来可以工作,而且在这个引擎中你可以从实例化中制作所谓的预制件GameObjects,如果这个 GameObject 包含通用组件,Unity3D 引擎将需要支持某种烘焙类型,而在实践中,这将导致生成脚本,每个脚本都有不同的类型,并在项目中造成大混乱。希望大家关注我

但为什么它适用于您标记为绿色的组件?简单地导致 Unity3D 引擎在将此组件添加到 GameObject 时知道所有类型。

要支持所有这些,Unity Technologies 需要对 Unity3D 引擎及其现在的工作方式进行核心更改。它将使 Unity3D 完全不同于现在的引擎。

所以要解决你的问题,只有一种方法:不添加运行时通用组件,去掉DataMgrBaseclass。所以你需要在每个组件中实现 DataMgrBase 逻辑。

我自己解决了如下:

更改基础 class 以获得新的泛型(class 的泛型类型,将从它派生,并将此类型传递给单例基础 class)

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

对于所有其他三个 class 想要从它派生的,将它们更改为以下形式:

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