简体   繁体   English

从泛型类继承,该类也继承自泛型单例

[英]Inheriting from generic class that also inherits from a generic singleton

I'm having difficulty inheriting from a generic class that is itself inherited from a generic singleton.我很难从泛型类继承,而泛型类本身是从泛型单例继承的。

I am trying to make an inventory base class that is a singleton and derive different inventory types from this with different derived items.我正在尝试制作一个单件库存基类,并从中使用不同的派生项目派生出不同的库存类型。

Code has been simplified for brevity.为简洁起见,代码已被简化。

public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
    private static bool m_ShuttingDown = false;
    private static object m_Lock = new object();
    private static T m_Instance;

    public static T Instance
    {
        get
        {
            if (m_ShuttingDown)
                return null;
            lock (m_Lock)
            {
                if (m_Instance == null)
                {
                    m_Instance = (T)FindObjectOfType(typeof(T));    
                    if (m_Instance == null)
                    {
                        var singletonObject = new GameObject();
                        m_Instance = singletonObject.AddComponent<T>();
                        singletonObject.name = typeof(T).ToString() + " (Singleton)";

                        DontDestroyOnLoad(singletonObject);
                    }
                }    
                return m_Instance;
            }
        }
    }
    private void OnApplicationQuit()
    {
        m_ShuttingDown = true;
    }
    private void OnDestroy()
    {
        m_ShuttingDown = true;
    }
}

public class Inventory<T> : Singleton<Inventory<T>> where T : Item
{   
    ...
}
public class EquipmentInventory : Inventory<Equipment>
{
    ...
}
public class Item : ScriptableObject
{
    public string Name = "Item";
}
public class Equipment : Item
{
    public string Name = "Equipment";
}

I can't access the Instance;我无法访问实例;

private EquipmentInventory equipmentInventory;
private Inventory<Item> inventory;

public void Run()
{
    var cachedInventory = Inventory<Item>.Instance;  //returns null
    var cachedEquipmentInventory = EquipmentInventory.Instance as EquipmentInventory;  //returns null
}

Both statements return null.两个语句都返回 null。

The purpose of this, is that each inventory type will be a singleton and each type of inventory will be implent different item types, so that the base inventory will use the Item type, while the Equipment inventory will be implemented using the Equipment item type.这样做的目的是,每个库存类型都是一个单件,每种类型的库存将实现不同的项目类型,因此基础库存将使用项目类型,而设备库存将使用设备项目类型实现。

Here is an alternate method, which seems to solve this这是一种替代方法,它似乎解决了这个问题

public abstract class Inventory<T, TClass> 
        : Singleton<TClass> where TClass 
        : MonoBehaviour where T : Item
    {

    }
public class EquipmentInventory : Inventory<Equipment, EquipmentInventory>
    {

    }

I haven't fully tested this yet with actual code, but will update when I have tested it more thoroughly我还没有用实际代码完全测试过这个,但是当我更彻底地测试它时会更新

Please assist.请协助。

The main issue is caused by主要问题是由

public class Inventory<T> : Singleton<Inventory<T>> : where T : Item { }

Here you "pass in" the generic type Iventory<T> into the Singleton even though later you explicitly inherit from that Inventory<T> class.在这里,您将泛型类型Iventory<T> “传入”到Singleton即使稍后您显式继承了该Inventory<T>类。


Here is one possible solution though it might seem like a bit strange workaround at first:这是一个可能的解决方案,尽管起初看起来有点奇怪:

Make your Inventory take a second generic type, use the first one inside the class as needed and "forward" the second one to the Singleton<T> like eg使您的 Inventory 采用第二个泛型类型,根据需要在类中使用第一个,并将第二个“转发”到Singleton<T> ,例如

// Note how for the limitation via where you still can use the generic type
// which makes sure no other MonoBehaviour can be passed to TSelf by accident
public class Inventory<TItem, TSelf> : Singleton<TSelf> where TItem : Item where TSelf : Inventory<TItem,TSelf>
{
    public TItem reference;

    private void Awake()
    {
        if (!reference) reference = ScriptableObject.CreateInstance<TItem>();
    }
}

Then in the implementation your pass additionally in your own final (non-generic) type so it can be properly "forwarded" to the Singleton<T> like eg然后在实现中,你的通行证另外在你自己的最终(非通用)类型中,所以它可以正确地“转发”到Singleton<T>像例如

public class EquipmentInventory : Inventory<Equipment, EquipmentInventory> { }

Note that anyway this class has to be in a separated file called EquipmentInventory.cs otherwise it won't work as component in Unity.请注意,无论如何,此类必须位于名为EquipmentInventory.cs的单独文件中,否则它将无法作为 Unity 中的组件运行。


This works now since now you explicitly pass in the type EquipmentInventory for TSelf which is then forwarded to the Signleton<T> so the return type of Instance is explicitly EquipmentInventory .这现在有效,因为现在您显式传递了TSelf EquipmentInventory类型,然后将其转发到Signleton<T>因此Instance的返回类型明确为EquipmentInventory


In general get used to rather have one script file for each individual class/type.一般来说,习惯于为每个单独的类/类型使用一个脚本文件。

Additionally I would slightly alter your fields in Item and Equipment like eg另外我会稍微改变你在ItemEquipment领域,比如

[CreateAssetMenu]
public class Item : ScriptableObject
{
    [SerializeField] private string _name;

    public string Name => _name;

    private void Awake()
    {
        _name = GetName();
    }

    protected virtual string GetName()
    {
       return nameof(Item);
    }
}

and

[CreateAssetMenu]
public class Equipment : Item
{
    protected override string GetName()
    {
        return nameof(Equipment);
    }
}

And this is how it looks like eg这就是它的样子,例如

public class Example : MonoBehaviour
{
    public EquipmentInventory equipmentInventory;

    [ContextMenu("Run")]
    public void Run()
    {
        equipmentInventory = EquipmentInventory.Instance;
    }
}

在此处输入图片说明

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM