繁体   English   中英

为什么 IReadOnlyCollection 有 ElementAt 但没有 IndexOf

[英]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 )中.

但是,是的,它仍然是很奇怪的,即使你认为这种方式是希望它要么两者ElementAtIndexOf无。

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM