【问题标题】:How to make Unity singleton base class to support generics?如何使 Unity 单例基类支持泛型?
【发布时间】:2016-05-05 07:04:59
【问题描述】:

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

绿色类是旧代码,运行良好。红框中的类是新添加的代码。没有编译错误,但是在Unity中点击play运行到新代码时,三个类不能正确初始化。

并且统一控制台发出警告说“名为'DataMgrBase`2'的类是通用的。不支持通用的MonoBehaviours!UnityEngine.GameObject:AddComponent()”在这一行:“instance = obj.AddComponent();”

我该如何解决这个问题?

以下是一些代码供您参考,谢谢!

单例基类的实现:

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

【问题讨论】:

    标签: c# generics unity3d unity5


    【解决方案1】:

    我自己解决了如下:

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

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

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

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

    【讨论】:

    • 这是一个很酷的解决方法,但我有一个小小的感觉,它完全不是 Unity3D 的做事方式。无论如何祝贺你解决了这个问题!
    • 谢谢 :) @JerrySwitalski
    【解决方案2】:

    你想要这样的东西:

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

    然后在你的具体类中:

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

    【讨论】:

    • 这与 OP 有何不同?
    • 谢谢!试过了,还是一样的警告。在“instance = obj.AddComponent ();”处初始化仍然失败
    【解决方案3】:

    这是某种无法解决您的问题的答案,但会解释问题。

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

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

    2. 使用 AddComponent&lt;T&gt; 其中T 是通用的看起来可以工作,但在这个引擎中你也可以用实例化的游戏对象制作所谓的预制件,如果这个游戏对象包含通用组件Unity3D 引擎需要支持某种烘焙类型,实际上这会导致生成脚本,每个脚本都有不同的类型,并在项目内部造成很大的混乱。希望大家关注我。

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

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

    因此,要解决您的问题,只有一种方法:不添加运行时通用组件,并摆脱 DataMgrBase 类。所以你需要在每个组件中实现DataMgrBase 逻辑。

    【讨论】:

    • 感谢分享,我已经解决了。请检查我的答案。泛型类型在编译后不会改变,因此它实际上不是“运行时”泛型。
    猜你喜欢
    • 1970-01-01
    • 2021-12-06
    • 1970-01-01
    • 2017-09-04
    • 2020-07-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多