可能的重复项:
中
在 C# 中,为什么不能将 List对象存储在 List
为什么以下方法不起作用?
List<string> castMe = new List<string>();
IEnumerable<string> getFromCast = (IEnumerable<string>)castMe; // allowed.
Dictionary<int, List<string>> castMeDict = new Dictionary<int, List<string>>();
Dictionary<int, IEnumerable<string>> getFromDict = (Dictionary<int, IEnumerable<string>>)castMeDict; // Not allowed
这是Dictionary
铸造机制的缺陷,还是我认为应该允许这样做?
谢谢。
这是字典转换机制中的缺陷,还是我认为应该允许这样做?
在你的思维中。您期望字典在其转换中应该是协变的。他们不是,原因如下。假设他们是,并推断出可能出错的地方:
Dictionary<int, List<string>> castMeDict =
new Dictionary<int, List<string>>();
Dictionary<int, IEnumerable<string>> getFromDict =
(Dictionary<int, IEnumerable<string>>)castMeDict;
castMeDict[123] = new List<string>();
IEnumerable<string> strings = getFromDict[123]; // No problem!
getFromDict[123] = new string[] { "hello" }; // Big problem!
字符串数组可转换为IEnumerable<string>
但不能转换为List<string>
。您只需将不是字符串列表的内容放入只能接受字符串列表的字典中。
在 C# 中,如果满足以下所有条件,泛型类型可以是协变或逆变:
- 你使用的是 C# 4 或更高版本。
- 不同的泛型类型是接口或委托。
- 方差可证明是类型安全的。(C# 规范描述了我们用于确定方差安全性的规则。C# 4.0 版本文档文件可以下载 [这里]。请参阅第 23.5 节。
- 变化的类型参数都是引用类型。
- 该类型已被特别标记为对差异安全。
字典不满足这些条件中的大多数 - 它不是接口或委托,它不是可证明的安全,并且类型未标记为对方差安全。因此,字典没有差异。
相比之下,IEnumerable<T>
确实满足所有这些条件。可以在 C# 4 中将IEnumerable<string>
转换为IEnumerable<object>
。
如果您对方差主题感兴趣,请考虑阅读我关于该主题的二十几篇文章:
http://blogs.msdn.com/b/ericlippert/archive/tags/covariance+and+contravariance/
研究逆变和协方差。有关为什么这种情况可能是一件坏事的特定示例,请查看Jon Skeet的答案。
想想如果允许会发生什么,然后你做了:
getFromDict.Add(34, new HashSet<string>);
这是完全允许的; HashSet<string>
实现IEnumerable<string>
,可以作为值添加到Dictionary<int, IEnumerable<string>>
。它不能被添加到一个Dictionary<int, List<string>>
,而这个对象才是真正的。
如果你想把它用作只读IDictionary<int, IEnumerable<string>>
那么你可以从一个包装类中获得很好的效率,该包装类在运行时强制转换为IEnumerable<string>
。
否则,您需要将值复制到新Dictionary<int, IEnumerable<string>>
中。