2017-11-18 198 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