为什么IReadOnlyCollection具有ElementAt但不具有IndexOf

时间:2016-05-25 08:37:30

标签: c# linq extension-methods readonly-collection

我正在使用moc个对象。

现在我有点惊讶,因为我可以使用IReadOnlyCollection扩展方法linq。但我无法访问ElementAt()

这对我来说看起来有点不合逻辑:我可以在给定位置获取元素,但我无法获得该元素的位置。

是否有特定原因?

我已经阅读了 - > How to get the index of an element in an IEnumerable?我对回应并不满意。

5 个答案:

答案 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)

IndexOfList上定义的方法,而IReadOnlyCollection仅继承IEnumerable

这是因为Enumerable仅用于迭代实体。然而,索引不适用于这种概念,主要是因为顺序是任意的,并且在调用Enumerable之间不保证是相同的。此外,界面只是声明你可以迭代一个集合,而List表示你也可以执行添加和删除。

ElementOf - 方法肯定会这样做。但是我不会使用它,因为它重新迭代整个枚举以找到一个单独的元素。更好地使用First或仅使用基于列表的方法。

无论如何,API设计对我来说似乎很奇怪,因为它允许(低效)方法在第n个位置获取元素但是不允许获得任意元素的索引,这将导致向上的低效率到n次迭代。我和Ian一起去(我不推荐)或者没有。

答案 2 :(得分:3)

这是因为IReadOnlyCollection(实现IEnumerable)并不一定实现indexing,这通常是您想要在数字上订购List时所需要的。 IndexOf来自IList

考虑一个没有索引的集合,例如DictionaryDictionary中没有数字索引的概念。在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;
}