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

use*_*667 9 c# generics dictionary casting covariance

可能重复:
在C#中,为什么List <string>对象不能存储在List <object>变量中

以下为什么不工作?

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
Run Code Online (Sandbox Code Playgroud)

这是Dictionary铸造机制中的一个缺陷,还是我认为应该允许这样做?

谢谢.

Eri*_*ert 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!
Run Code Online (Sandbox Code Playgroud)

字符串数组可以转换为IEnumerable<string>但不能转换为List<string>.您只需将不是字符串列表的内容放入只能获取字符串列表的字典中.

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

  • 您正在使用C#4或更高版本.
  • 变化的泛型类型是接口或委托.
  • 方差可证明是类型安全的.(C#规范描述了我们用于确定方差安全性的规则.C#4.0版本doc文件可以[这里]下载.请参阅第23.5节.)
  • 变化的类型参数都是引用类型.
  • 该类型已被特别标记为方差安全.

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

IEnumerable<T>相比之下,确实符合所有这些条件.你可以转换IEnumerable<string>IEnumerable<object>C#4.

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

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

  • @ user420667:是的.现在,如果有办法说"我保证在转换之后我只会*从这本词典中读取*,而且,我只会*通过阅读字典从我获得的任何对象中读取*,并且所以"然后我们可以使它安全协变.但是除了*使字典支持只保证安全*的只读接口之外,没有办法在CLR类型系统中表达这种承诺,也没有办法强制执行它.这就是为什么你可以将`List <string>`视为`IEnumerable <object>` - 因为'IE <o>`无法写*. (2认同)