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/24012667/

相关文章:

c# - 如何将文件从 UNC 共享复制到本地系统?

html - 图标字体与 SVG 缓存和网络问题

mysql - "SELECT COUNT(*)"很慢,即使有 where 子句

c# - 使用 linq 时返回匿名类型结果

c# - CosmosDb LINQ 查询 'any' 不起作用,但 'count() > 0' 起作用?

c# - 如何使用 EF5 和存储库检查给定 ID 的记录是否已存在

c# - 通过 SSL 保护 Xamarin.Forms 应用程序流量

c# - 以下 LINQ 语句如何工作?

c++ - 在 C++ 中创建大量对象

c# - (Xamarin,Android)此代码如何帮助减少引用实例?