c# - Skip 的性能(以及类似的功能,如 Take)

标签 c# performance linq ienumerable skip-take

我刚刚查看了 Skip 的源代码/Take .NET Framework 的扩展方法(在 IEnumerable<T> 类型上),发现内部实现正在使用 GetEnumerator方法:

// .NET framework
    public static IEnumerable<TSource> Skip<TSource>(this IEnumerable<TSource> source, int count)  
    {
        if (source == null) throw Error.ArgumentNull("source"); 
        return SkipIterator<TSource>(source, count); 
    }

    static IEnumerable<TSource> SkipIterator<TSource>(IEnumerable<TSource> source, int count) 
    {
        using (IEnumerator<TSource> e = source.GetEnumerator()) 
        {
            while (count > 0 && e.MoveNext()) count--;
            if (count <= 0) 
            { 
                while (e.MoveNext()) yield return e.Current;
            } 
        } 
    }

假设我有一个 IEnumerable<T>有 1000 个元素(基础类型是 List<T> )。如果我正在执行 list.Skip(990).Take(10) 会发生什么?它会在取最后十个元素之前遍历 990 个第一个元素吗? (这就是我的理解)。如果是,那么我不明白为什么 Microsoft 没有实现 Skip像这样的方法:

    // Not tested... just to show the idea
    public static IEnumerable<T> Skip<T>(this IEnumerable<T> source, int count)
    {
        if (source is IList<T>)
        {
            IList<T> list = (IList<T>)source;
            for (int i = count; i < list.Count; i++)
            {
                yield return list[i];
            }
        }
        else if (source is IList)
        {
            IList list = (IList)source;
            for (int i = count; i < list.Count; i++)
            {
                yield return (T)list[i];
            }
        }
        else
        {
            // .NET framework
            using (IEnumerator<T> e = source.GetEnumerator())
            {
                while (count > 0 && e.MoveNext()) count--;
                if (count <= 0)
                {
                    while (e.MoveNext()) yield return e.Current;
                }
            }
        }
    }

事实上,他们这样做是为了 Count方法例如...

    // .NET Framework...
    public static int Count<TSource>(this IEnumerable<TSource> source) 
    {
        if (source == null) throw Error.ArgumentNull("source");

        ICollection<TSource> collectionoft = source as ICollection<TSource>; 
        if (collectionoft != null) return collectionoft.Count;

        ICollection collection = source as ICollection; 
        if (collection != null) return collection.Count; 

        int count = 0;
        using (IEnumerator<TSource> e = source.GetEnumerator())
        { 
            checked 
            {
                while (e.MoveNext()) count++;
            }
        } 
        return count;
    } 

那是什么原因呢?

最佳答案

在 Jon Skeet 的优秀教程中重新实现 Linq ,他(简短地)讨论了这个问题:

Although most of these operations can't be sensibly optimized, it would make sense to optimize Skip when the source implements IList. We can skip the skipping, so to speak, and go straight to the appropriate index. This wouldn't spot the case where the source was modified between iterations, which may be one reason it's not implemented in the framework as far as I'm aware.

这似乎是推迟优化的合理理由,但我同意对于特定情况,如果您可以保证您的源不能/不会被修改,那么进行优化可能是值得的。

关于c# - Skip 的性能(以及类似的功能,如 Take),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20002975/

相关文章:

excel - 宏运行时突然变慢

c# - LINQ-从包含三层的列表中检索对象的第三层列表

c# - Linq.Enumerable 中的索引越界

c# - Entity Framework 在没有标识列的表上不起作用

具有完整名称的 C# Enum.ToString()

c# - 如何在 csv 列中使用逗号

c# - 通过方法创建具有通用 csv 阅读器的类

java - 递归和循环哪个更有效?

performance - 从 UIWebView 中删除 HTML 元素时性能糟糕

c# - 替换 DataColumn 中的值