【问题标题】:Creating Dictionaries of generic types创建泛型类型的字典
【发布时间】:2026-02-17 03:55:01
【问题描述】:

在我的示例中,我有许多扩展基类 Fruit 的类(OrangePearApple、...)。

我正在创建一个模型类,其中包含映射到其整数 ID 的每种类型的 Fruit 的字典。我想避免像这样制作许多字段变量:

Dictionary<int, Orange> _oranges = new Dictionary<int, Orange>();

我想我可以创建一个“通用”字典,在其中我将其他字典映射到 Fruit 类型:

Dictionary<Type, Dictionary<int, Fruit>> _fruits = new Dictionary<Type, Dictionary<int, Fruit>>();

要插入到这个结构中,我使用如下方法:

public void Insert(Fruit fruit)
{
    _fruits[fruit.GetType()][fruit.Id] = fruit;
}

当我尝试检索存储的值时,问题就出现了,就像在这个方法中一样:

public IEnumerable<T> GetFruits<T>() where T : Fruit
{
    return (IEnumerable<T>) _fruits[typeof(T)].Values.ToArray();
}

这将被称为GetFruits&lt;Orange&gt;()。转换失败并出现此错误:

System.InvalidCastException: '无法转换类型的对象 'Example.Fruit[]' 输入 'System.Collections.Generic.IEnumerable`1[Example.Orange]'。'

我该如何做我想做的事?

【问题讨论】:

    标签: c# .net dictionary generics


    【解决方案1】:

    我认为您只需使用 Cast&lt;T&gt; 进行投射即可。

    return _fruits[typeof(T)].Values.Cast<T>();
    

    使用(IEnumerable&lt;T&gt;) 进行投射不起作用,因为它会投射整个内容。您可能已经知道这一点:List&lt;object&gt; 不能转换为 List&lt;int&gt;。同样的现象也发生在这里。

    因此,我们应该使用Cast&lt;T&gt;。此方法会将可枚举的每个元素强制转换为指定的类型,并返回生成的可枚举。

    【讨论】:

    • 另外/或者,您还可以使用OfType 过滤类型并避免可能的 InvalidCast 异常
    • 谢谢,这正是我想要的,我明白为什么我的代码以前不能工作:)
    【解决方案2】:

    你可以使用OfType方法:

    var _fruits = new Dictionary<Type, Dictionary<int, Fruit>>();
    
    public IEnumerable<T> GetFruits<T>() where T : Fruit
    {
        return _fruits.OfType<T>().ToArray();
    }
    

    【讨论】:

      【解决方案3】:

      你得到错误的原因是因为_fruits的最内层字典中Values的类型本质上是基类Fruit的映射:

      Dictionary<int, Fruit>
      

      具体来说,Values property is defined as ICollection&lt;T&gt;

      在运行时,您不能直接重铸 Values,即 ICollection&lt;Fruit&gt;IEnumerable&lt;T&gt; - 例如IEnumerable&lt;Orange&gt;.

      要解决此问题,您实际上需要遍历 Values 集合并按类型向下转换(可能还包括过滤器)。

      (即使你“知道”你的代码只允许Oranges 进入fruits[typeof(Orange)] 字典,从类型系统的角度来看,类型仍然是ICollection&lt;Fruit&gt;

      根据其他答案,您可以使用任意数量的方法进行此转换和过滤:

      • 过滤的 foreach,foreach(T item in Values)
      • .Cast&lt;T&gt; - 但是,如果以某种方式找到不同的水果,它会抛出
      • .OfType&lt;T&gt; - 这将排除不正确类型的项目。

      这些方法的更多细节discussed here

      【讨论】: