簡體   English   中英

從泛型類繼承,該類也繼承自泛型單例

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

我很難從泛型類繼承,而泛型類本身是從泛型單例繼承的。

我正在嘗試制作一個單件庫存基類,並從中使用不同的派生項目派生出不同的庫存類型。

為簡潔起見,代碼已被簡化。

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

我無法訪問實例;

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
}

兩個語句都返回 null。

這樣做的目的是,每個庫存類型都是一個單件,每種類型的庫存將實現不同的項目類型,因此基礎庫存將使用項目類型,而設備庫存將使用設備項目類型實現。

這是一種替代方法,它似乎解決了這個問題

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

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

    }

我還沒有用實際代碼完全測試過這個,但是當我更徹底地測試它時會更新

請協助。

主要問題是由

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

在這里,您將泛型類型Iventory<T> “傳入”到Singleton即使稍后您顯式繼承了該Inventory<T>類。


這是一個可能的解決方案,盡管起初看起來有點奇怪:

使您的 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>();
    }
}

然后在實現中,你的通行證另外在你自己的最終(非通用)類型中,所以它可以正確地“轉發”到Singleton<T>像例如

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

請注意,無論如何,此類必須位於名為EquipmentInventory.cs的單獨文件中,否則它將無法作為 Unity 中的組件運行。


這現在有效,因為現在您顯式傳遞了TSelf EquipmentInventory類型,然后將其轉發到Signleton<T>因此Instance的返回類型明確為EquipmentInventory


一般來說,習慣於為每個單獨的類/類型使用一個腳本文件。

另外我會稍微改變你在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);
    }
}

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

這就是它的樣子,例如

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