我正在使用moc
个对象。
现在我有点惊讶,因为我可以使用IReadOnlyCollection
扩展方法linq
。但我无法访问ElementAt()
。
这对我来说看起来有点不合逻辑:我可以在给定位置获取元素,但我无法获得该元素的位置。
是否有特定原因?
我已经阅读了 - > How to get the index of an element in an IEnumerable?我对回应并不满意。
答案 0 :(得分:22)
简单明了:
IndexOf()
无法用于IReadOnlyList<T>
,没有任何充分的理由 。如果您真的想找到提及的理由,那么原因是历史性的:
上世纪90年代中期,C#被放下时,人们还没有开始意识到不变性和只读性的好处,因此不幸的是,他们融入该语言的IList<T>
接口是可变的。 / p>
正确的做法是提出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在评论中指出这一点。)
答案 1 :(得分:4)
IndexOf
是List
上定义的方法,而IReadOnlyCollection
仅继承IEnumerable
。
这是因为Enumerable
仅用于迭代实体。然而,索引不适用于这种概念,主要是因为顺序是任意的,并且在调用Enumerable
之间不保证是相同的。此外,界面只是声明你可以迭代一个集合,而List
表示你也可以执行添加和删除。
ElementOf
- 方法肯定会这样做。但是我不会使用它,因为它重新迭代整个枚举以找到一个单独的元素。更好地使用First
或仅使用基于列表的方法。
无论如何,API设计对我来说似乎很奇怪,因为它允许(低效)方法在第n个位置获取元素但是不允许获得任意元素的索引,这将导致向上的低效率到n次迭代。我和Ian一起去(我不推荐)或者没有。
答案 2 :(得分:3)
这是因为IReadOnlyCollection
(实现IEnumerable
)并不一定实现indexing
,这通常是您想要在数字上订购List
时所需要的。 IndexOf
来自IList
。
考虑一个没有索引的集合,例如Dictionary
,Dictionary
中没有数字索引的概念。在Dictionary
中,订单无法保证,密钥和值之间只有一对一的关系。因此,收集并不一定意味着数字索引。
另一个原因是因为IEnumerable
实际上并不是两种流量。可以这样考虑:IEnumerable
可以按照您的指定枚举项x
次,并在x
找到元素(即ElementAt
),但它无法有效地知道如果它的任何元素位于哪个索引(即IndexOf
)。
但是,是的,即使你认为这样也是非常奇怪的,因为它希望 <{1}}和ElementAt
或无。
答案 3 :(得分:2)
IReadOnlyCollection<T>
具有ElementAt<T>()
,因为它是具有该方法的IEnumerable<T>
的扩展。 ElementAt<T>()
在IEnumerable<T>
上迭代指定的迭代次数,并返回该位置的值。
IReadOnlyCollection<T>
缺少IndexOf<T>()
,因为作为IEnumerable<T>
,它没有任何指定的顺序,因此索引的概念不适用。 IReadOnlyCollection<T>
也不添加任何顺序概念。
当您需要IReadOnlyList<T>
的可索引版本时,我建议使用IReadOnlyCollection<T>
。这样,您就可以正确地用索引表示对象的不变集合。
答案 4 :(得分:0)
这可能对某人有所帮助
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;
}