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

C# Object类型字典中子类方法调用及泛型List转换问题

问题解决:List子类集合转父类集合的InvalidCastException

问题背景

我有一个存储List的Dictionary,List中的元素是继承自Item父类的Weapon、Armour、Potion子类,子类重写了GetItemData方法。Player类的inventory字典定义如下:

internal abstract class Item
{
    protected string name = "Item";
    protected int level = GameConstants.MIN_ITEM_LEVEL;
    protected string description = "";
    protected Types type;

    public enum Types
    {
        armour, potion, weapon
    }

    public Dictionary<string, dynamic> GetItemData()
    {
        return new Dictionary<string, dynamic>() {
            { "name",  name},
            { "description", description },
            { "type", type }
        };
    }

}
// Player类中的字典
protected Dictionary<string, object> inventory
        = new()
        {
            {"WEAPON", new List<Weapon>() },
            {"AMOUR", new List<Armour>() },
            {"POTION", new List<Potion>() }
            // 所有子类都继承自Item,比如class Weapon:Item
        };

编写ListPlayerItems方法展示物品数据时,抛出异常:

System.InvalidCastException: "Unable to cast object of type 'System.Collections.Generic.List`1[Items.Weapon.Weapon]' to type 'System.Collections.Generic.List`1[Items.Item]'."

方法代码:

private void ListPlayerItems(string previewText, string itemSelector)
{
    Console.WriteLine(previewText);
    int itemIndex = 0;

    ((List<Item>)inventory[itemSelector]).ForEach(item =>
    {
        Dictionary<string, dynamic> itemData = item.GetItemData();
        Console.Write($"{itemIndex}) 名称: {itemData["name"]}; 描述:{itemData["description"]}; ");
        if (item is Weapon)
        {
            Console.Write($"最小伤害: {itemData["minDamage"]}; 最大伤害: {itemData["maxDamage"]}\n");
        }

        itemIndex++;
    });
}

问题出在((List<Item>)inventory[itemSelector])的类型转换,需要解决这个转换问题或优化实现方案。

错误原因

List<T>是不变泛型类型,即使Weapon继承自Item,List<Weapon>也不能直接转换为List<Item>——因为泛型不变性不允许这样的向上转换,编译器无法保证转换后的集合操作安全性(比如向List<Item>中添加Armour,但原集合是List<Weapon>,会导致类型错误)。

解决方案

方案1:利用协变接口转换为IEnumerable

IEnumerable<out T>是协变泛型接口,支持将子类集合转换为父类的IEnumerable。修改转换代码即可:

private void ListPlayerItems(string previewText, string itemSelector)
{
    Console.WriteLine(previewText);
    int itemIndex = 0;

    // 先转成IEnumerable<Item>,而非List<Item>
    ((IEnumerable<Item>)inventory[itemSelector]).ToList().ForEach(item =>
    {
        Dictionary<string, dynamic> itemData = item.GetItemData();
        Console.Write($"{itemIndex}) 名称: {itemData["name"]}; 描述:{itemData["description"]}; ");
        if (item is Weapon)
        {
            Console.Write($"最小伤害: {itemData["minDamage"]}; 最大伤害: {itemData["maxDamage"]}\n");
        }

        itemIndex++;
    });
}

如果不需要List的额外方法,甚至可以直接遍历IEnumerable,不用转ToList:

foreach (var item in (IEnumerable<Item>)inventory[itemSelector])
{
    // 逻辑同前
    itemIndex++;
}

方案2:重构inventory的类型为Dictionary<string, List>

直接将inventory的value类型定义为List<Item>,因为子类实例可以直接添加到父类List中,从根源避免类型转换:

// 修改Player类中的inventory定义
protected Dictionary<string, List<Item>> inventory
        = new()
        {
            {"WEAPON", new List<Item>() },
            {"AMOUR", new List<Item>() },
            {"POTION", new List<Item>() }
        };

// 添加物品时直接添加子类实例,比如:
inventory["WEAPON"].Add(new Weapon());

这样ListPlayerItems方法中无需转换,直接使用:

inventory[itemSelector].ForEach(item => { /* ... */ });

方案3:使用OfType筛选元素

如果必须保留原inventory的Dictionary<string, object>类型,可以先将object转换为IEnumerable,再用OfType过滤出父类元素:

private void ListPlayerItems(string previewText, string itemSelector)
{
    Console.WriteLine(previewText);
    int itemIndex = 0;

    var items = ((IEnumerable)inventory[itemSelector]).OfType<Item>();
    foreach (var item in items)
    {
        Dictionary<string, dynamic> itemData = item.GetItemData();
        Console.Write($"{itemIndex}) 名称: {itemData["name"]}; 描述:{itemData["description"]}; ");
        if (item is Weapon)
        {
            Console.Write($"最小伤害: {itemData["minDamage"]}; 最大伤害: {itemData["maxDamage"]}\n");
        }

        itemIndex++;
    }
}

内容的提问来源于stack exchange,提问作者Даниил Дуров

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 18:20:48