前一阵子我写了一个IList扩展方法,通过使用索引来枚举列表的一部分。重构时,我意识到可以通过调用Skip(toSkip).Take(amount)来执行类似的查询。在对此进行基准测试时,我注意到Skip并未针对IList进行优化。经过一番谷歌搜索,我最终发现了Jon Skeet的帖子discussing why optimizing methods like Skip is dangerous

据我了解,本文的问题不在于修改集合时在优化方法中抛出异常,但有一条注释指出msdn文档本身发生冲突。

IEnumerator.MoveNext()中:



IEnumerator.GetEnumerator()中:



我看到这两种约定都有优点,并且无论是否进行优化都有些失落。什么是适当的解决方案?正如Kris Vandermotten在评论中提到的那样,我一直在考虑采用IList.AssumeImmutable()方法。是否已经存在任何实现,或者这是一个坏主意?

最佳答案

我同意Rafe的观点,即不确定行为更为正确。只有版本化的集合可以引发异常,而并非所有集合都被版本化(数组是最大的示例)。如果您在MoveNext的调用之间进行了2 ^ 32次准确的更改,则即使是版本控制的集合也可能会出现异常。

假设您真的关心版本控制行为,解决方案是获取EnumeratorIList,并为每次迭代调用MoveNext:

    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++];
    }

通过这种方式,您可以通过索引获得O(1)行为,但是仍然可以获得调用MoveNext的所有异常抛出行为。请注意,我们仅将MoveNext称为异常副作用。我们忽略其枚举的值。

关于c# - 为IList优化LINQ,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/5862503/

10-13 03:28