C# 为什么可以';当可以从一个值类型转换为另一个值类型时,我是否将一个值类型的字典转换为另一个值类型的字典?

C# 为什么可以';当可以从一个值类型转换为另一个值类型时,我是否将一个值类型的字典转换为另一个值类型的字典?,c#,generics,dictionary,casting,covariance,C#,Generics,Dictionary,Casting,Covariance,可能重复: 为什么下面的代码不起作用 List<string> castMe = new List<string>(); IEnumerable<string> getFromCast = (IEnumerable<string>)castMe; // allowed. Dictionary<int, List<string>> castMeDict = new Dictionary<int, List<s

可能重复:

为什么下面的代码不起作用

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
List castMe=new List();
IEnumerable getFromCast=(IEnumerable)castMe;//允许。
Dictionary castMeDict=新字典();
字典getFromDict=(字典)castMeDict;//不准
这是
词典
转换机制中的一个缺陷,还是我认为应该允许这样做


谢谢。

研究逆变换和协方差。关于为什么这种情况可能是件坏事的一个具体例子,请查看。

想想如果允许的话会发生什么,然后您就这样做了:

getFromDict.Add(34, new HashSet<string>);
getFromDict.Add(34,新哈希集);
这是完全允许的
HashSet
实现
IEnumerable
,并可以作为值添加到
字典中。虽然不能将它添加到
字典中
,但该对象实际上就是这样的

如果您想将其用作只读的
IDictionary
,那么您可以从一个包装器类中获得很好的效率,该包装器类可以按原样转换为
IEnumerable

否则,您需要将这些值复制到新的
字典中

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

在你看来。您希望字典在转换时应该是协变的。由于以下原因,它们不是。假设是这样,并推断出可能出现的问题:

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!
字典castMeDict=
新字典();
字典getFromDict=
(词典)castMeDict;
castMeDict[123]=新列表();
IEnumerable strings=getFromDict[123];//没问题!
getFromDict[123]=新字符串[]{“hello”};//大问题!
字符串数组可转换为
IEnumerable
,但不能转换为
列表
。您只需将非字符串列表的内容放入只能获取字符串列表的字典中

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

  • 您正在使用C#4或更高版本
  • 不同的泛型类型是接口或委托
  • 方差是可证明的类型安全的。(C#规范描述了我们用于确定差异安全性的规则。可以下载C#4.0版本的文档文件。请参阅第23.5节。)
  • 不同的类型参数都是引用类型
  • 该类型已被特别标记为安全变异
对于dictionary,这些条件中的大多数都不满足——它不是接口或委托,它不可证明安全,并且该类型没有标记为方差安全。因此,字典没有差异

相比之下,IEnumerable确实满足所有这些条件。您可以在C#4中将
IEnumerable
转换为
IEnumerable

如果你感兴趣的话题,请考虑读我的两篇关于这个问题的文章:


见:谢谢大家。抱歉,我确实检查了重复的实例,但在不知道答案的情况下,首先概括然后搜索所有可能重复的特定实例有点困难。实际上这正是我需要的效率,谢谢。包装器的想法?你清楚该怎么做吗?我想这就像你创建了一个实现IDictionary的类,它将字典作为构造函数,将其作为成员保留,并重写[]和get操作符。你就是这么想的吗?谢谢,就是这样。实现
可能是最棘手的一点,不过如果它只在程序集内部使用,那么您可以抛出
NotImplementedException()
,这样您就知道它不会被调用。对于编写代码的成员,您可以选择创建一个新的
列表
(如果可以强制转换,请先进行测试)或抛出
NotSupportedException
,如果您为
IsReadOnly
返回true,则可以接受该选项。显然,System.Linq.Lookup类就是我们要寻找的,但我不确定构建它是否会影响性能。我明白了。所以问题是castMeDict仍然存在,尽管我最初只是用它来构建字典。@user420667:Yep。现在,如果有一种方式可以说“我保证,我只会在转换后阅读这本词典,而且,我只会阅读通过阅读词典获得的任何对象,等等”,那么我们可以使它安全协变。但在CLR类型的系统中,除了使字典支持一个保证安全的只读接口之外,没有办法表达这种承诺,也没有办法强制执行它。这就是为什么您可以将
列表
视为
IEnumerable
——因为
IE
无法写入.Hm。我想如果我只打算使用对象进行阅读,那么它不应该是一个真正的字典,而是一个特殊的类似字典的类,它限制了setter。有点像Jon Hanna的建议。我不知道为什么我没有想到建议通过
void ProcessDictionary(dictionary dict)接收字典,其中t:IEnumerable
。不会适用于所有情况,但会适用于很多情况。