![](/img/trans.png)
[英]Why HashSet<T> does not implement IReadOnlyCollection<T>?
[英]Why IReadOnlyCollection has ElementAt but not IndexOf
我正在使用IReadOnlyCollection
对象。
现在我有点惊讶,因为我可以使用linq
扩展方法ElementAt()
。 但我无权访问IndexOf()
。
这在我看来有点不合逻辑:我可以在给定位置获取元素,但我无法获取该元素的位置。
有什么具体原因吗?
我已经阅读过 -> 如何获取 IEnumerable 中元素的索引? 我对回应并不完全满意。
IReadOnlyList<T>
没有任何理由没有IndexOf()
。如果真要找个理由提起,那是历史原因:
早在 90 年代中期 C# 诞生时,人们还没有完全意识到不变性和只读的好处,因此不幸的是,他们融入语言的IList<T>
接口是可变的。
正确的做法是将IReadOnlyList<T>
作为基本接口,并使IList<T>
扩展它,仅添加突变方法,但事实并非如此。
IReadOnlyList<T>
是在IList<T>
之后很长时间发明的,到那时重新定义IList<T>
并使其扩展IReadOnlyList<T>
为时已晚。 因此, IReadOnlyList<T>
是从头开始构建的。
他们不能让IReadOnlyList<T>
扩展IList<T>
,因为那样它会继承变异方法,所以他们基于IReadOnlyCollection<T>
和IEnumerable<T>
代替。 他们添加了this[i]
索引器,但随后他们要么忘记添加其他方法,如IndexOf()
,要么故意省略它们,因为它们可以作为扩展方法实现,从而使接口更简单。 但是他们没有提供任何这样的扩展方法。
所以,这里是一个将IndexOf()
添加到IReadOnlyList<T>
的扩展方法:
using Collections = System.Collections.Generic;
public static int IndexOf<T>( this Collections.IReadOnlyList<T> self, T elementToFind )
{
int i = 0;
foreach( T element in self )
{
if( Equals( element, elementToFind ) )
return i;
i++;
}
return -1;
}
请注意,此扩展方法不如接口中内置的方法强大。 例如,如果您正在实现一个期望IEqualityComparer<T>
作为构造(或其他独立)参数的集合,则此扩展方法将完全不知道它,这当然会导致错误。 (感谢 Grx70 在评论中指出这一点。)
IndexOf
是一个在List
定义的方法,而IReadOnlyCollection
只继承了IEnumerable
。
这是因为IEnumerable
仅用于迭代实体。 然而,索引不适用于这个概念,因为顺序是任意的,并且不能保证在调用IEnumerable
之间是相同的。 此外,界面只是声明您可以迭代集合,而List
声明您也可以执行添加和删除操作。
ElementAt
方法确实做到了这一点。 但是我不会使用它,因为它会重复整个枚举以找到一个元素。 更好地使用First
或仅使用基于列表的方法。
无论如何,API 设计对我来说似乎很奇怪,因为它允许在第n个位置获取元素的(低效)方法,但不允许获取任意元素的索引,这与导致最多n次迭代的低效搜索相同. 我同意 Ian 的观点(我不推荐)或两者都不同意。
这是因为IReadOnlyCollection
(实现IEnumerable
)不一定实现indexing
,当您想要对List
进行数字排序时通常需要indexing
。 IndexOf
来自IList
。
认为,如果没有指数喜欢收藏的Dictionary
例如,没有在数字指标的概念Dictionary
。 在Dictionary
,顺序是没有保证的,只有 key 和 value 之间的一一对应关系。 因此,集合并不一定意味着数字索引。
另一个原因是因为IEnumerable
并不是真正的两种方式的流量。 可以这样想: IEnumerable
可能会在您指定时枚举项x
次并在x
处找到元素(即ElementAt
),但它无法有效地知道它的任何元素是否位于哪个索引(即IndexOf
)中.
但是,是的,它仍然是很奇怪的,即使你认为这种方式是希望它要么两者ElementAt
和IndexOf
或无。
IReadOnlyCollection<T>
具有ElementAt<T>()
因为它是IEnumerable<T>
的扩展,它具有该方法。 ElementAt<T>()
在IEnumerable<T>
上迭代指定次数的迭代并返回值作为该位置。
IReadOnlyCollection<T>
缺少IndexOf<T>()
因为作为IEnumerable<T>
,它没有任何指定的顺序,因此索引的概念不适用。 IReadOnlyCollection<T>
也不添加任何顺序概念。
当您想要IReadOnlyList<T>
的可索引版本时,我会推荐IReadOnlyList<T>
IReadOnlyCollection<T>
。 这允许您使用索引正确表示不可更改的对象集合。
这可能对某人有帮助:
public static int IndexOf<T>(this IReadOnlyList<T> self, Func<T, bool> predicate)
{
for (int i = 0; i < self.Count; i++)
{
if (predicate(self[i]))
return i;
}
return -1;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.