为IList优化LINQ

Ste*_*ris 9 .net c# linq optimization skip

不久之前,我编写了一个IList扩展方法,通过使用索引来枚举列表的一部分.在重构的过程中,我意识到可以通过调用来执行类似的查询Skip(toSkip).Take(amount).在对此进行基准测试时,我发现它Skip没有针对进行优化IList.通过一些谷歌搜索,我最终在Jon Skeet的帖子,讨论为什么优化方法Skip是危险的.

据我理解这篇文章,问题是在修改集合时抛出优化方法没有异常,但是作为注释声明msdn文档本身冲突.

IEnumerator.MoveNext()中:

如果对集合进行了更改,例如添加,修改或删除元素,则枚举数将无法恢复,并且下一次调用MoveNext或Reset 会引发InvalidOperationException.

IEnumerator.GetEnumerator()中:

如果对集合进行了更改(例如添加,修改或删除元素),则枚举数将无法恢复,并且其行为未定义.

我认为两种惯例都有用,而且无论是否进行优化都会有点失落.什么是正确的解决方案?我一直在考虑采用Kris Vandermotten在评论中提到的IList.AssumeImmutable()方法AsParallel().是否已存在任何实现,或者这是一个坏主意?

Gab*_*abe 3

我同意 Rafe 的观点,即未定义的行为更正确。只有版本化的集合才能抛出异常,并且并非所有集合都被版本化(数组是最大的例子)。如果您在MoveNext.

假设您确实关心版本控制行为,解决方案是获取EnumeratorforIListMoveNext在每次迭代时调用它:

    public static IEnumerable<T> Skip<T>(this IList<T> source, int count)
    {
        using (var e = source.GetEnumerator())
            while (count < source.Count && e.MoveNext())
                yield return source[count++];
    }
Run Code Online (Sandbox Code Playgroud)

这样,您可以通过索引获得 O(1) 行为,但仍然可以获得调用MoveNext. 请注意,我们只调用MoveNext异常副作用;我们忽略它枚举的值。