为什么 `IList<T>` 不继承自 `IReadOnlyList<T>`?
Why doesn't `IList<T>` inherit from `IReadOnlyList<T>`?
当 .NET 4.5 中引入 IReadOnlyList<T>
时,有那么一刻我认为拼图的缺失部分终于被插入到位:一种传递真正的只读可索引接口的方法,而以前我必须这样做使用我自己的只读界面并围绕所有内容创建包装器 类。
我希望界面位于“自然”层次结构中,理想情况下是:
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>
而无需 包装它的方法。这就是我发现的问题。
我还认为 IReadOnlyCollection
的专有名称应该是 ICountable
,IReadOnlyList
的专有名称应该是 IIndexable
:
IEnumerable<T>
.GetEnumerator()
-> ICountable<T> : IEnumerable<T>
.Count
-> IIndexable<T> : ICountable<T>
.Item[...]
-> IList<T> : IIndexable<T>
.Add(...)
.Clear()
.Contains(...)
(etc)
@w.b 在包含答案的评论中放一个 link 到 New interfaces IReadOnlyList and IReadOnlyDictionary:
Why did we not change the existing interfaces to extend the read-only interfaces?
It looks like a reasonable assumption that it works because the read-only interfaces are purely a subset of the read-write interfaces. Unfortunately, it is incompatible because at the metadata level every method on every interface has its own slot (which makes explicit interface implementations work).
Immo Landwerth | .NET Framework Team (BCL) | http://blogs.msdn.com/b/bclteam/
为了更清楚地解释这一点:
假设为 .NET 4.0 编写的程序包含实现 IList<T>
的 class MyList<T>
。它显然无法实现 IReadOnlyList<T>
,因为该接口不存在。
现在假设系统管理员安装了 .NET 4.5 并假设 .NET 4.5 使 IList<T>
实现了 IReadOnlyList<T>
.
如果随后加载程序,运行时将检测到 MyList<T>
声称实现了 IList<T>
,但实际上并没有实现所有方法:它没有实现 [=12] =]的方法。该程序将不再有效。
C# 编译器可能能够按名称匹配方法,但运行时不会这样做。由于 .NET 4.5 应该具有向后二进制兼容性,因此无法扩展接口以实现其他接口,即使这些其他接口包含所需方法的严格子集也是如此。
当 .NET 4.5 中引入 IReadOnlyList<T>
时,有那么一刻我认为拼图的缺失部分终于被插入到位:一种传递真正的只读可索引接口的方法,而以前我必须这样做使用我自己的只读界面并围绕所有内容创建包装器 类。
我希望界面位于“自然”层次结构中,理想情况下是:
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>
而无需 包装它的方法。这就是我发现的问题。
我还认为 IReadOnlyCollection
的专有名称应该是 ICountable
,IReadOnlyList
的专有名称应该是 IIndexable
:
IEnumerable<T>
.GetEnumerator()
-> ICountable<T> : IEnumerable<T>
.Count
-> IIndexable<T> : ICountable<T>
.Item[...]
-> IList<T> : IIndexable<T>
.Add(...)
.Clear()
.Contains(...)
(etc)
@w.b 在包含答案的评论中放一个 link 到 New interfaces IReadOnlyList and IReadOnlyDictionary:
Why did we not change the existing interfaces to extend the read-only interfaces?
It looks like a reasonable assumption that it works because the read-only interfaces are purely a subset of the read-write interfaces. Unfortunately, it is incompatible because at the metadata level every method on every interface has its own slot (which makes explicit interface implementations work).
Immo Landwerth | .NET Framework Team (BCL) | http://blogs.msdn.com/b/bclteam/
为了更清楚地解释这一点:
假设为 .NET 4.0 编写的程序包含实现 IList<T>
的 class MyList<T>
。它显然无法实现 IReadOnlyList<T>
,因为该接口不存在。
现在假设系统管理员安装了 .NET 4.5 并假设 .NET 4.5 使 IList<T>
实现了 IReadOnlyList<T>
.
如果随后加载程序,运行时将检测到 MyList<T>
声称实现了 IList<T>
,但实际上并没有实现所有方法:它没有实现 [=12] =]的方法。该程序将不再有效。
C# 编译器可能能够按名称匹配方法,但运行时不会这样做。由于 .NET 4.5 应该具有向后二进制兼容性,因此无法扩展接口以实现其他接口,即使这些其他接口包含所需方法的严格子集也是如此。