为啥 List<T> 在 .NET 4.5 中实现 IReadOnlyList<T>?

Posted

技术标签:

【中文标题】为啥 List<T> 在 .NET 4.5 中实现 IReadOnlyList<T>?【英文标题】:Why does List<T> implement IReadOnlyList<T> in .NET 4.5?为什么 List<T> 在 .NET 4.5 中实现 IReadOnlyList<T>? 【发布时间】:2013-02-22 04:00:11 【问题描述】:

为什么List&lt;T&gt; 在.NET 4.5 中实现IReadOnlyList&lt;T&gt;

List&lt;T&gt; 不是只读的...

【问题讨论】:

【参考方案1】:

因为List&lt;T&gt; 实现了所有必要的方法/属性/等。 (然后是一些)IReadOnlyList&lt;T&gt;。接口是一个契约,上面写着“我可以至少做这些事情。”

IReadOnlyList&lt;T&gt; 的文档说它代表一个只读的元素集合。

没错。该接口中没有 mutator 方法。这就是只读的意思,对吧? IReadOnlyList&lt;T&gt; 以“典型”(合同)方式使用,而不是 marker。

【讨论】:

界面不保证列表不能被修改。这只是意味着你不能通过界面修改它。 按照这个逻辑,那么 IList 不应该实现 IReadOnlyList 吗?我认为这很愚蠢。 应该!但它不能,因为这将是一个突破性的变化。阅读我之前提供的链接。 类型和子类型关系的“是”解释对于开始 OO 编程很有用。但是,它的适用范围还不是很远。 “这就是只读的意思,对吧?”——嗯,不。我不是律师,但我相当确定,“only”的合同与“至少”的合同大不相同。我>”。该术语在IListIsReadOnly 属性中正确应用,当读取 写入是可能的时false。接口应该命名为IIndexable【参考方案2】:

接口仅描述将要实现的功能。它没有描述将不会实现的功能。因此,IReadOnlyList 是一个不正确的接口名称,因为它不能规定不会写入写入功能。

describe 的方法/功能可以读取列表的内容。接口应该是 IReadableList 而不是 IReadOnlyList。

【讨论】:

对此肯定有话要说。 我同意;名称的“唯一”部分令人不安。【参考方案3】:

实现接口与“标记”接口不同。 List&lt;T&gt; 也实现了 IEnumerable&lt;T&gt;,但这并不意味着您仅限于简单地枚举它。

他们为 API 创建添加了只读接口,而不是让您可以使用接口标记只读类型。当我只想知道集合中元素的数量而不枚举它时,它允许我使用IReadOnlyCollection&lt;T&gt; 作为参数,或者当我需要通过索引引用集合中的元素时使用IReadOnlyList&lt;T&gt;。这对每个人都有好处——我可以具体说明我的调用者需要什么,同时允许我的调用者使用他想要的任何集合类型,只要它符合我通过参数类型设置的最低标准。

所以我认为更困难的问题是,为什么不会你让List&lt;T&gt; 实现IReadOnlyList&lt;T&gt;

【讨论】:

***.com/questions/12622539/… 提出了类似的“为什么不会你”的问题。答案归结为“这样做会导致反向兼容性损坏”。这让我很难过。【参考方案4】:

它实现接口的事实并不意味着它是只读的。但是因为它实现了接口,您现在可以将它传递给期望IReadOnlyList&lt;T&gt; 的方法。所以看待它的方式是,它实现了只读列表接口......以及一些写入方法。

【讨论】:

【参考方案5】:

接口IReadOnlyListIReadOnlyCollection 有点令人困惑,因为它们并不意味着集合是只读的,只是支持只读访问。来自MSDN documentation(向下滚动到备注)

列表元素的内容不保证是只读的。

更好的名字是IReadable,见Why doesn't generic ICollection implement IReadOnlyCollection in .NET 4.5?。此外,这意味着IList 应该继承IReadOnlyList,尽管它不是由于向后兼容性,请参阅Why doesn't IList&lt;T&gt; inherit from IReadOnlyList&lt;T&gt;?。

【讨论】:

【参考方案6】:

IReadOnlyList 是“引用不变性”概念的替代品,该概念在 C++ 中找到,但在 C# 中没有。 C++ 等价物是:

void func(T const* t) ...

或完全等效,如某些人喜欢的那样:

void func(const T* t) ...

它表示函数 func 不会改变其参数 t 引用的对象,称为 t 的“所指对象”。它没有说明任何其他代码是否会改变 t 的所指对象,甚至可以。

因此,C# 接口是编译器构造的替代品。为什么 C# 没有引用不变性的概念是一个历史问题:我认为这是一个错误,但现在修复它为时已晚。我认为提供接口替代品很好。多年来,我一直在使用与 IReadOnlyList 完全相同的接口,幸运的是使用了另一个名称 IConstList。我可以用 IReadOnlyList 代替我对 IConstList 的使用。

【讨论】:

【参考方案7】:

运行 IReadOnlyList(Of T) 调用实现该接口的对象的代码通常运行相同的执行线程,这不是真的吗?这可以防止对象自行更改,除非它在不同的执行线程上运行,但对于这种情况,我们有同步调用来解决这个问题。

【讨论】:

以上是关于为啥 List<T> 在 .NET 4.5 中实现 IReadOnlyList<T>?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我在 List<List<T>> 中复制 List<T> 时存在依赖关系? [复制]

为啥 List<T>.ForEach 允许修改其列表?

为啥 List<T> 的“索引超出范围”异常,但数组却没有?

为啥对 List<T> 的迭代没有从 SQL 视图中给我正确的值? [复制]

为啥 std::min(std::initializer_list<T>) 按值接受参数?

为啥 C# 数组对 Enumeration 使用引用类型,而 List<T> 使用可变结构?