为啥 `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<T>
在 .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<T>
并不继承自 IReadOnlyList<T>
。
这是有原因的吗?
一些澄清:
请注意,IReadOnlyList<T>
只是一个合同,它声明列表提供了一种获取列表计数和读取某个索引处的值的方法。它的名字很糟糕,因为它并没有强制实际实现是只读的。
List<T>
实现 IEnumerable<T>
,IList<T>
继承自 IEnumerable<T>
,但这并不意味着这些类只能被枚举。
所以,如果你想将一个列表传递给一个方法,并且只允许它被索引(读取)而不是被修改,你需要将它包装在一个新的实例中。同时,您可以将其传递给接受IEnumerable<T>
或IList<T>
而无需 的方法。这就是我发现的坏处。
我还认为正确的名称应该是 ICountable
代表 IReadOnlyCollection
和 IIndexable
代表 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<>
实现了IList<>
但它不是只读的,那么为什么要实现IReadOnlyList<>
?。
@TimSchmelter List<>
实际上实现了IReadOnlyList<>
。而且这个问题不是问List<>
,而是问IList<>
。
这实际上很烦人,因为我不能只在 IReadOnlyList<T>
上编写扩展方法,也不能同时为 IList<T>
和 IReadOnlyList<T>
编写它们,因为那样你会得到模棱两可的方法错误.嘎。
【参考方案1】:
@w.b 在包含答案的 cmets 中添加指向 New interfaces IReadOnlyList and IReadOnlyDictionary 的链接:
为什么我们不改变现有的接口来扩展只读接口?
它看起来是一个合理的假设,因为只读接口纯粹是读写接口的子集。不幸的是,它是不兼容的,因为在元数据级别,每个接口上的每个方法都有自己的槽(这使得显式接口实现工作)。
伊莫兰德韦斯 | .NET 框架团队 (BCL) | http://blogs.msdn.com/b/bclteam/
为了更清楚地解释这一点:
假设为 .NET 4.0 编写的程序包含一个实现 IList<T>
的类 MyList<T>
。它显然无法实现IReadOnlyList<T>
,因为该接口不存在。
现在假设系统管理员安装了 .NET 4.5,并假设 .NET 4.5 使 IList<T>
实现 IReadOnlyList<T>
。
如果程序随后被加载,运行时会检测到MyList<T>
声称实现了IList<T>
,但实际上并未实现所有方法:它没有实现IReadOnlyList<T>
的方法。该程序将不再起作用。
C# 编译器可能能够按名称匹配方法,但运行时不这样做。由于 .NET 4.5 应该具有向后二进制兼容性,因此无法扩展接口以实现其他接口,即使这些其他接口包含所需方法的严格子集也是如此。
【讨论】:
+1 另一个示例基于引用对显式实现的引用。如果一个类已经有一个显式的int IList<T>.Count ...
实现并且IList<T>
将继承自IReadOnlyList<T>
,则 Count 将属于后者并且现有的实现将失败,即使使用正确的框架构建。
@Me.Name 这也是一个很好的例子,尽管我认为可以通过扩展 C# 来解决这个问题。
.net-5.x 是否愿意破坏 ABI 并修复此问题?
@binki 据我了解,.NET 5 已被重新命名为 .NET Core(但如果我错了,有人可以纠正我,这实际上是另外一回事)。在 .NET Core 中,IList<T>
目前仍未实现 IReadOnlyList<T>
。
@hvd:我认为 .NET Core 的目标之一是在合理的情况下保持 API 兼容性,以便尽可能轻松地迁移 代码(如果不是二进制文件的话) 到/从完整框架,但我不确定...以上是关于为啥 `IList<T>` 不从 `IReadOnlyList<T>` 继承?的主要内容,如果未能解决你的问题,请参考以下文章