c# - 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.
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.
解决方案
主要问题是由
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 中的组件工作。
这现在有效,因为现在您显式传入类型EquipmentInventory
,TSelf
然后将其转发给,Signleton<T>
所以返回类型Instance
是显式的EquipmentInventory
。
通常习惯于为每个单独的类/类型拥有一个脚本文件。
另外我会稍微改变你的领域Item
,Equipment
比如
[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;
}
}
推荐阅读
- python - PyQt5 QDateEdit,仅未显示月份和年份
- python - AttributeError:“JWTManager”对象没有属性“token_in_blacklist_loader”
- gitlab - Gitlab管道规则:当分支有打开的合并请求时如何不运行作业?防止重复管道
- php - 在 Laravel 8 中找不到类“App\Models\BlogPostCategory”
- java - 如何在 Spring Boot 中使用加密的有效负载
- android - 应用程序无法全屏运行?无论是在模拟器上还是在手机上
- docker-compose - 出现错误 0901:身份验证失败
- backup - Oracle Free Cloud 恢复启动卷备份
- javascript - 使用 xlsx 库下载数据并将其写入 xlsx 文件
- mongodb - Mongoose:按计算字段查找