[英]Why IReadOnlyCollection has ElementAt but not IndexOf
I am working with a IReadOnlyCollection
of objects.我正在使用
IReadOnlyCollection
对象。
Now I'm a bit surprised, because I can use linq
extension method ElementAt()
.现在我有点惊讶,因为我可以使用
linq
扩展方法ElementAt()
。 But I don't have access to IndexOf()
.但我无权访问
IndexOf()
。
This to me looks a bit illogical: I can get the element at a given position, but I cannot get the position of that very same element.这在我看来有点不合逻辑:我可以在给定位置获取元素,但我无法获取该元素的位置。
Is there a specific reason for it?有什么具体原因吗?
I've already read -> How to get the index of an element in an IEnumerable?我已经阅读过 -> 如何获取 IEnumerable 中元素的索引? and I'm not totally happy with the response.
我对回应并不完全满意。
IReadOnlyList<T>
has no IndexOf()
for no good reason whatsoever . IReadOnlyList<T>
没有任何理由没有IndexOf()
。 If you really want to find a reason to mention, then the reason is historical:如果真要找个理由提起,那是历史原因:
Back in the mid-nineties when C# was laid down, people had not quite started to realize the benefits of immutability and readonlyness, so the IList<T>
interface that they baked into the language was, unfortunately, mutable.早在 90 年代中期 C# 诞生时,人们还没有完全意识到不变性和只读的好处,因此不幸的是,他们融入语言的
IList<T>
接口是可变的。
The right thing would have been to come up with IReadOnlyList<T>
as the base interface, and make IList<T>
extend it, adding mutation methods only, but that's not what happened.正确的做法是将
IReadOnlyList<T>
作为基本接口,并使IList<T>
扩展它,仅添加突变方法,但事实并非如此。
IReadOnlyList<T>
was invented a considerable time after IList<T>
, and by that time it was too late to redefine IList<T>
and make it extend IReadOnlyList<T>
. IReadOnlyList<T>
是在IList<T>
之后很长时间发明的,到那时重新定义IList<T>
并使其扩展IReadOnlyList<T>
为时已晚。 So, IReadOnlyList<T>
was built from scratch.因此,
IReadOnlyList<T>
是从头开始构建的。
They could not make IReadOnlyList<T>
extend IList<T>
, because then it would have inherited the mutation methods, so they based it on IReadOnlyCollection<T>
and IEnumerable<T>
instead.他们不能让
IReadOnlyList<T>
扩展IList<T>
,因为那样它会继承变异方法,所以他们基于IReadOnlyCollection<T>
和IEnumerable<T>
代替。 They added the this[i]
indexer, but then they either forgot to add other methods like IndexOf()
, or they intentionally omitted them since they can be implemented as extension methods, thus keeping the interface simpler.他们添加了
this[i]
索引器,但随后他们要么忘记添加其他方法,如IndexOf()
,要么故意省略它们,因为它们可以作为扩展方法实现,从而使接口更简单。 But they did not provide any such extension methods.但是他们没有提供任何这样的扩展方法。
So, here, is an extension method that adds IndexOf()
to IReadOnlyList<T>
:所以,这里是一个将
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;
}
Be aware of the fact that this extension method is not as powerful as a method built into the interface would be.请注意,此扩展方法不如接口中内置的方法强大。 For example, if you are implementing a collection which expects an
IEqualityComparer<T>
as a construction (or otherwise separate) parameter, this extension method will be blissfully unaware of it, and this will of course lead to bugs.例如,如果您正在实现一个期望
IEqualityComparer<T>
作为构造(或其他独立)参数的集合,则此扩展方法将完全不知道它,这当然会导致错误。 (Thanks to Grx70 for pointing this out in the comments.) (感谢 Grx70 在评论中指出这一点。)
IndexOf
is a method defined on List
, whereas IReadOnlyCollection
inherits just IEnumerable
. IndexOf
是一个在List
定义的方法,而IReadOnlyCollection
只继承了IEnumerable
。
This is because IEnumerable
is just for iterating entities.这是因为
IEnumerable
仅用于迭代实体。 However an index doesn't apply to this concept, because the order is arbitrary and is not guaranteed to be identical between calls to IEnumerable
.然而,索引不适用于这个概念,因为顺序是任意的,并且不能保证在调用
IEnumerable
之间是相同的。 Furthermore the interface simply states that you can iterate a collection, whereas List
states you can perform adding and removing also.此外,界面只是声明您可以迭代集合,而
List
声明您也可以执行添加和删除操作。
The ElementAt
method sure does exactly this. ElementAt
方法确实做到了这一点。 However I won't use it as it reiterates the whole enumeration to find one single element.但是我不会使用它,因为它会重复整个枚举以找到一个元素。 Better use
First
or just a list-based approach.更好地使用
First
或仅使用基于列表的方法。
Anyway the API design seems odd to me as it allows an (inefficient) approach on getting an element at n -th position but does not allow to get the index of an arbitrary element which would be the same inefficient search leading to up to n iterations.无论如何,API 设计对我来说似乎很奇怪,因为它允许在第n个位置获取元素的(低效)方法,但不允许获取任意元素的索引,这与导致最多n次迭代的低效搜索相同. I'd agree with Ian on either both (which I wouldn't recommend) or neither.
我同意 Ian 的观点(我不推荐)或两者都不同意。
It is because the IReadOnlyCollection
(which implements IEnumerable
) does not necessarily implement indexing
, which often required when you want to numerically order a List
.这是因为
IReadOnlyCollection
(实现IEnumerable
)不一定实现indexing
,当您想要对List
进行数字排序时通常需要indexing
。 IndexOf
is from IList
. IndexOf
来自IList
。
Think of a collection without index like Dictionary
for example, there is no concept of numeric index in Dictionary
.认为,如果没有指数喜欢收藏的
Dictionary
例如,没有在数字指标的概念Dictionary
。 In Dictionary
, the order is not guaranteed, only one to one relation between key and value.在
Dictionary
,顺序是没有保证的,只有 key 和 value 之间的一一对应关系。 Thus, collection does not necessarily imply numeric indexing.因此,集合并不一定意味着数字索引。
Another reason is because IEnumerable
is not really two ways traffic.另一个原因是因为
IEnumerable
并不是真正的两种方式的流量。 Think of it this way: IEnumerable
may enumerate the items x
times as you specify and find the element at x
(that is, ElementAt
), but it cannot efficiently know if any of its element is located in which index (that is, IndexOf
).可以这样想:
IEnumerable
可能会在您指定时枚举项x
次并在x
处找到元素(即ElementAt
),但它无法有效地知道它的任何元素是否位于哪个索引(即IndexOf
)中.
But yes, it is still pretty weird even you think it this way as would expect it to have either both ElementAt
and IndexOf
or none.但是,是的,它仍然是很奇怪的,即使你认为这种方式是希望它要么两者
ElementAt
和IndexOf
或无。
IReadOnlyCollection<T>
has ElementAt<T>()
because it is an extension to IEnumerable<T>
, which has that method. IReadOnlyCollection<T>
具有ElementAt<T>()
因为它是IEnumerable<T>
的扩展,它具有该方法。 ElementAt<T>()
iterates over the IEnumerable<T>
a specified number of iterations and returns value as that position. ElementAt<T>()
在IEnumerable<T>
上迭代指定次数的迭代并返回值作为该位置。
IReadOnlyCollection<T>
lacks IndexOf<T>()
because, as an IEnumerable<T>
, it does not have any specified order and thus the concept of an index does not apply. IReadOnlyCollection<T>
缺少IndexOf<T>()
因为作为IEnumerable<T>
,它没有任何指定的顺序,因此索引的概念不适用。 Nor does IReadOnlyCollection<T>
add any concept of order. IReadOnlyCollection<T>
也不添加任何顺序概念。
I would recommend IReadOnlyList<T>
when you want an indexable version of IReadOnlyCollection<T>
.当您想要
IReadOnlyList<T>
的可索引版本时,我会推荐IReadOnlyList<T>
IReadOnlyCollection<T>
。 This allows you to correctly represent an unchangeable collection of objects with an index.这允许您使用索引正确表示不可更改的对象集合。
This could be helpful to someone:这可能对某人有帮助:
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.