2017-11-18 191 views
1

在我的示例中,我有很多扩展基类Fruit的类(Orange,Pear,Apple,...)。创建泛型类型的字典

我正在创建一个模型类,其中包含映射到其整数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<Orange>()。演员失败并出现此错误:

System.InvalidCastException: 'Unable to cast object of type 'Example.Fruit[]' to type 'System.Collections.Generic.IEnumerable`1[Example.Orange]'.'

我该怎么做我想做的事?

回答

2

我想你只需要使用Cast<T>就可以了。

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

使用(IEnumerable<T>)来投射不起作用,因为它会投射整个东西。您可能已经知道:List<object>不能铸造到List<int>。这里发生同样的现象。我们应该使用Cast<T>。此方法将把枚举的每个元素转换为指定的类型,并返回结果的枚举。

+0

另外/可替代地,也可以使用['OfType'](https://stackoverflow.com/a/41951319/314291)来过滤的类型,并避免可能的InvalidCast异常 – StuartLC

+0

谢谢,这正是我想要的,我明白为什么我的代码不工作之前:) –

1

您可以使用OfType方法:

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

public IEnumerable<T> GetFruits<T>() where T : Fruit 
{ 
    return _fruits.OfType<T>().ToArray(); 
} 
1

为什么你的错误是因为Values_fruits最里面的词典类型本身是地图的基类Fruit的原因:

Dictionary<int, Fruit> 

具体而言,Values property is defined as ICollection<T>

在运行时,您不允许直接重写Values,这是一个ICollection<Fruit>IEnumerable<T> - 例如, IEnumerable<Orange>

要解决此问题,您将有效地需要遍历Values集合,并按类型遍历(可能还会筛选)。

(即使你“知道”你的代码只允许Orangesfruits[typeof(Orange)]字典,从类型系统的角度来看,该类型仍然是ICollection<Fruit>

按其他的答案,你可以使用任意数量的方式来做到这一点铸造和过滤:

  • 一个过滤的foreach,foreach(T item in Values)
  • .Cast<T> - 然而,这将抛出,如果以某种方式不同的水果中发现
  • .OfType<T> - 这会排除错误类型的项目。

有一个关于这些方法更详细地discussed here