【问题标题】:Why is covariance not allowed with ReadOnlyCollection?为什么 ReadOnlyCollection 不允许协方差?
【发布时间】:2014-08-29 20:36:50
【问题描述】:

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

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

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

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

【问题讨论】:

  • “从数组中学到的一课”:关于数组协方差,这实际上是一个有意识的设计决定允许它,因为 Java 有它。结果证明这是一个非常糟糕的主意...blogs.msdn.com/b/ericlippert/archive/2007/10/17/…
  • @ThomasLevesque 很好,谢谢。

标签: c# covariance readonly-collection


【解决方案1】:

变体和逆变仅适用于接口和委托,不适用于类。

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

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

(因为IReadOnlyCollection&lt;T&gt; 是协变的)


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

类中的类型参数没有差异

只有接口和委托可以有变体类型参数。甚至 如果您有一个仅使用类型参数作为输入的类(或 仅用于输出),您不能指定 inout 修饰符。比如Comparer&lt;T&gt;,常见的实现 IComparer&lt;T&gt;,是不变的——没有从 Comparer&lt;IShape&gt;Comparer&lt;Circle&gt;

除了这可能带来的任何实施困难 发生,我会说它在概念上具有一定的意义。 接口代表一种从特定对象查看对象的方式 透视,而类更植根于对象的实际 类型。继承让你有点削弱了这个论点 将对象视为其中任何类的实例 不可否认,继承层次结构。无论哪种方式,CLR 都不允许 它。

【讨论】:

  • 你是对的。我对其进行了测试,它适用于只读接口IReadOnlyCollection&lt;T&gt;IReadOnlyList&lt;T&gt;。为什么它不适用于类有什么特别的原因吗?
  • @B.K.,我不知道,但我猜它与一些 CLR 内部实现细节有关......
  • 实现对接口的支持可能更容易。当您从一种具体类型转换为基本具体类型时,现在基本类型必须决定是调用其方法还是调用基本类型,这取决于仅在实际继承中定义的虚拟/覆盖/隐藏场景。 ReadOnlyCollection 不是从 ReadOnlycollection 继承的,因此很难定义会发生什么。接口总是将调用传递给具体类型,并且对于调用基类还是派生类没有歧义。访问协变接口的含义要清楚得多。海事组织
  • @AaronLS,这可能是正确的解释。很高兴您发布了它,因为 Google 对此没有太大帮助...
  • @AaronLS 这是一个很好的解释,完全有道理。非常感谢。
猜你喜欢
  • 2022-01-19
  • 2017-11-23
  • 2019-06-01
  • 2014-03-01
  • 2018-05-28
  • 1970-01-01
  • 2019-05-03
  • 2014-12-14
  • 1970-01-01
相关资源
最近更新 更多