You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将泛型基类Equipment<T>的不同子类存入同一列表?

这个问题其实是C#泛型不变性导致的——默认情况下,泛型类不会因为类型参数的继承关系而产生子类关系。哪怕WeaponData继承自EquipmentData,Equipment<WeaponData>也不会被看作Equipment<EquipmentData>的子类,所以直接用List<Equipment<EquipmentData>>肯定加不进去Weapon或者Shield对象。

给你两个最实用的解决方案,根据你的业务场景选:

方案1:定义非泛型基接口/基类

这是最通用的方案,不管你的泛型类有没有输入输出限制都能用。我们先搞一个不带泛型的基接口,让所有泛型Equipment<T>都实现它:

// 非泛型接口,定义所有装备共有的行为/属性
public interface IEquipment
{
    // 比如所有装备都有基础数据,这里用基类类型暴露
    EquipmentData BaseData { get; }
    void Use(); // 假设所有装备都有使用方法
}

// 泛型基类实现这个接口
public abstract class Equipment<T> : IEquipment where T : EquipmentData
{
    public T Data { get; protected set; }

    // 显式实现接口,把泛型Data转成基类类型
    EquipmentData IEquipment.BaseData => Data;

    // 实现通用方法,也可以留抽象让子类实现
    public abstract void Use();
}

// 你的子类不需要改,继续正常继承
public class Weapon : Equipment<WeaponData>
{
    public override void Use()
    {
        // 武器的使用逻辑
    }
}

public class Shield : Equipment<ShieldData>
{
    public override void Use()
    {
        // 盾牌的使用逻辑
    }
}

然后你的列表就可以写成:

List<IEquipment> equipmentInventory = new List<IEquipment>();
equipmentInventory.Add(new Weapon());
equipmentInventory.Add(new Shield());

这样既保证了类型安全,又能统一操作所有装备(比如调用Use()方法,或者访问BaseData)。

方案2:利用泛型接口的协变性

如果你的Equipment<T>主要是输出数据(比如只暴露T类型的属性,没有接收T作为参数的方法),那可以用C#的协变接口。协变允许我们把IEquipment<WeaponData>隐式转换成IEquipment<EquipmentData>,因为WeaponData是EquipmentData的子类。

代码示例:

// 用out关键字标记协变类型参数T,T只能作为返回值
public interface IEquipment<out T> where T : EquipmentData
{
    T Data { get; }
    // 注意:不能有这样的方法:void SetData(T data); 因为T作为输入的话协变不允许
}

public abstract class Equipment<T> : IEquipment<T> where T : EquipmentData
{
    public T Data { get; protected set; }
}

这时候列表就可以这么定义:

List<IEquipment<EquipmentData>> equipmentList = new List<IEquipment<EquipmentData>>();
equipmentList.Add(new Weapon()); // 现在可以正常添加了!
equipmentList.Add(new Shield());

但要记住协变的限制:接口里的T只能出现在返回值的位置,不能作为方法的参数类型。如果你的装备类需要设置T类型的数据(比如SetData(T data)),那这个方案就不适用,得用方案1。

不推荐的方案:用List或List

虽然也能存所有对象,但完全失去了类型安全,编译时不会检查你加的是不是Equipment子类,而且访问成员的时候还要强制转换,很容易出运行时错误,除非是非常特殊的场景,否则别用。

总结一下:优先选方案1(通用无限制),如果满足协变条件(T只作为输出),方案2会更简洁。

内容的提问来源于stack exchange,提问作者Wolf

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:34:07