为啥 `IList<T>` 不从 `IReadOnlyList<T>` 继承?

Posted

技术标签:

【中文标题】为啥 `IList<T>` 不从 `IReadOnlyList<T>` 继承?【英文标题】:Why doesn't `IList<T>` inherit from `IReadOnlyList<T>`?为什么 `IList<T>` 不从 `IReadOnlyList<T>` 继承? 【发布时间】:2017-12-29 19:03:46 【问题描述】:

IReadOnlyList&lt;T&gt; 在 .NET 4.5 中被引入时,我一度认为谜题的缺失部分终于到位:一种传递真正只读可索引接口的方法,以前我必须使用我自己的 read -only 接口并围绕所有内容创建包装类。

我希望将界面放置在“自然”层次结构中,理想情况下是:

IEnumerable<T> 
.GetEnumerator()
      -> IReadOnlyCollection<T> : IEnumerable<T>
      .Count
            -> IReadOnlyList<T> : IReadOnlyCollection<T>
            .Item[...]
                     -> IList<T> : IReadOnlyList<T>
                     .Add(...)
                     .Clear()
                     .Contains(...)
                     (etc)

但是,事实证明,IList&lt;T&gt; 并不继承自 IReadOnlyList&lt;T&gt;

这是有原因的吗?

一些澄清:

请注意,IReadOnlyList&lt;T&gt; 只是一个合同,它声明列表提供了一种获取列表计数和读取某个索引处的值的方法。它的名字很糟糕,因为它并没有强制实际实现是只读的。

List&lt;T&gt; 实现 IEnumerable&lt;T&gt;IList&lt;T&gt; 继承自 IEnumerable&lt;T&gt;,但这并不意味着这些类只能被枚举。

所以,如果你想将一个列表传递给一个方法,并且只允许它被索引(读取)而不是被修改,你需要将它包装在一个新的实例中。同时,您可以将其传递给接受IEnumerable&lt;T&gt;IList&lt;T&gt; 而无需 的方法。这就是我发现的坏处。

我还认为正确的名称应该是 ICountable 代表 IReadOnlyCollectionIIndexable 代表 IReadOnlyList

IEnumerable<T> 
.GetEnumerator()
      -> ICountable<T> : IEnumerable<T>
      .Count
            -> IIndexable<T> : ICountable<T>
            .Item[...]
                     -> IList<T> : IIndexable<T>
                     .Add(...)
                     .Clear()
                     .Contains(...)
                     (etc)

【问题讨论】:

正如你已经说过的,IReadonlyList 是在 .NET4.5 中引入的,后来 IList 被添加到 .Net 中。 检查这个:social.msdn.microsoft.com/Forums/vstudio/en-US/… List&lt;&gt; 实现了IList&lt;&gt; 但它不是只读的,那么为什么要实现IReadOnlyList&lt;&gt;?。 @TimSchmelter List&lt;&gt; 实际上实现了IReadOnlyList&lt;&gt;。而且这个问题不是问List&lt;&gt;,而是问IList&lt;&gt; 这实际上很烦人,因为我不能只在 IReadOnlyList&lt;T&gt; 上编写扩展方法,也不能同时为 IList&lt;T&gt;IReadOnlyList&lt;T&gt; 编写它们,因为那样你会得到模棱两可的方法错误.嘎。 【参考方案1】:

@w.b 在包含答案的 cmets 中添加指向 New interfaces IReadOnlyList and IReadOnlyDictionary 的链接:

为什么我们不改变现有的接口来扩展只读接口?

它看起来是一个合理的假设,因为只读接口纯粹是读写接口的子集。不幸的是,它是不兼容的,因为在元数据级别,每个接口上的每个方法都有自己的槽(这使得显式接口实现工作)。


伊莫兰德韦斯 | .NET 框架团队 (BCL) | http://blogs.msdn.com/b/bclteam/

为了更清楚地解释这一点:

假设为 .NET 4.0 编写的程序包含一个实现 IList&lt;T&gt; 的类 MyList&lt;T&gt;。它显然无法实现IReadOnlyList&lt;T&gt;,因为该接口不存在。

现在假设系统管理员安装了 .NET 4.5,并假设 .NET 4.5 使 IList&lt;T&gt; 实现 IReadOnlyList&lt;T&gt;

如果程序随后被加载,运行时会检测到MyList&lt;T&gt; 声称实现了IList&lt;T&gt;,但实际上并未实现所有方法:它没有实现IReadOnlyList&lt;T&gt; 的方法。该程序将不再起作用。

C# 编译器可能能够按名称匹配方法,但运行时不这样做。由于 .NET 4.5 应该具有向后二进制兼容性,因此无法扩展接口以实现其他接口,即使这些其他接口包含所需方法的严格子集也是如此。

【讨论】:

+1 另一个示例基于引用对显式实现的引用。如果一个类已经有一个显式的int IList&lt;T&gt;.Count ... 实现并且IList&lt;T&gt; 将继承自IReadOnlyList&lt;T&gt;,则 Count 将属于后者并且现有的实现将失败,即使使用正确的框架构建。 @Me.Name 这也是一个很好的例子,尽管我认为可以通过扩展 C# 来解决这个问题。 .net-5.x 是否愿意破坏 ABI 并修复此问题? @binki 据我了解,.NET 5 已被重新命名为 .NET Core(但如果我错了,有人可以纠正我,这实际上是另外一回事)。在 .NET Core 中,IList&lt;T&gt; 目前仍未实现 IReadOnlyList&lt;T&gt; @hvd:我认为 .NET Core 的目标之一是在合理的情况下保持 API 兼容性,以便尽可能轻松地迁移 代码(如果不是二进制文件的话) 到/从完整框架,但我不确定...

以上是关于为啥 `IList<T>` 不从 `IReadOnlyList<T>` 继承?的主要内容,如果未能解决你的问题,请参考以下文章

如何序列化 IList<T>?

IList<T> 和 IReadOnlyList<T>

IList<T> 到 IQueryable<T>

将 IList<T> 转换为 BindingList<T>

如何对 IList<T> 执行二进制搜索?

如何转换通用IList IList?