9

此代码有效的原因是Enumerator无法修改集合:

var roList = new List<string>() { "One", "Two", "Three" };
IEnumerable<object> objEnum = roList;

如果我们尝试用 aList<object>而不是做同样的事情IEnumerable<object>,我们会得到一个编译器错误,告诉我们我们不能将 type 隐式转换List<string>List<object>。好吧,这是有道理的,在我看来,这是微软的一个很好的决定,作为从数组中吸取的教训。

但是,我不明白为什么这个强硬规则适用于类似的东西ReadOnlyCollection?我们将无法修改 中的元素ReadOnlyCollection,那么导致 Microsoft 阻止我们对只读内容使用协方差的安全问题是什么?有没有一种可能的方法来修改微软试图解释的那种类型的集合,比如通过指针?

4

1 回答 1

10

方差和逆变仅适用于接口和委托,而不适用于类。

但是,您可以这样做(使用 .NET 4.5):

ReadOnlyCollection<string> roList = ...
IReadOnlyCollection<object> objects = roList;

(因为IReadOnlyCollection<T>是协变的)


要回答您关于为什么类中不允许变化的问题,请参阅 Jon Skeet 在他的C# in Depth书中的解释(第二版,第 13.3.5 节,第 394 页)。

类中的类型参数没有差异

只有接口和委托可以有变体类型参数。即使您有一个仅将类型参数用于输入(或仅将其用于输出)的类,您也不能指定inorout 修饰符。例如Comparer<T>, , 的常见实现 IComparer<T>是不变的——没有从 Comparer<IShape>到的转换Comparer<Circle>

除了这可能带来的任何实施困难之外,我想说它在概念上具有一定的意义。接口代表从特定角度看待对象的一种方式,而类则更植根于对象的实际类型。不可否认,继承允许您将对象视为其继承层次结构中任何类的实例,这一论点在某种程度上被削弱了。无论哪种方式,CLR 都不允许这样做。

于 2014-08-29T20:41:09.037 回答