当值类型可以相互转换时,为什么我不能将一个值类型的字典转换为另一个值类型的字典?

时间:2011-12-19 20:34:58

标签: c# generics dictionary casting covariance

  

可能重复:
  In C#, why can't a List<string> object be stored in a List<object> variable

为什么以下不起作用?

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投射机制中的缺陷,还是我认为应该允许这样做?

感谢。

3 个答案:

答案 0 :(得分:22)

  

这是字典转换机制中的一个缺陷,还是我认为应该允许这样做?

在你的思考中。您期望词典在转换中应该协变。由于以下原因,它们不是。假设它们是,并推断出可能出现的问题:

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>。你只需将 not 的字符串列表放入一个只能获取字符串列表的字典中。

在C#中,如果满足以下所有条件,则泛型类型可以是协变的或逆变的:

  • 您使用的是C#4或更高版本。
  • 不同的泛型类型是接口或委托。
  • 差异可证明是类型安全的。 (C#规范描述了我们用于确定方差安全性的规则。可以下载C#4.0版本doc文件[here]。参见第23.5节。)
  • 不同的类型参数都是引用类型。
  • 该类型已被明确标记为方差安全。

字典中不满足大多数条件 - 它不是接口或委托,它不可证明是安全的,并且类型未标记为方差安全。所以,字典没有变化。

相比之下,

IEnumerable<T>符合所有这些条件。您可以在C#4中将IEnumerable<string>转换为IEnumerable<object>

如果变化的主题让您感兴趣,请考虑阅读我关于该主题的二十多篇文章:

http://blogs.msdn.com/b/ericlippert/archive/tags/covariance+and+contravariance/

答案 1 :(得分:4)

研究逆变和协方差。有关为什么这种情况可能不好的特定示例,请检查this answer by Jon Skeet

答案 2 :(得分:2)

考虑如果允许会发生什么,然后你做了:

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>>