c# - 为什么 WhereSelectArrayIterator 不实现 ICollection?

标签 c# performance list icollection toarray

在通过 Reflector 查看 System.Linq.Enumerable 时,我注意到默认迭代器用于 SelectWhere 扩展方法 - WhereSelectArrayIterator - 不实现 ICollection 接口(interface)。如果我正确阅读代码,这会导致一些其他扩展方法,例如 Count()ToList() 执行速度变慢:

public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
{
    // code above snipped
    if (source is List<TSource>)
    {
        return new WhereSelectListIterator<TSource, TResult>((List<TSource>) source, null, selector);
    }
    // code below snipped
}

private class WhereSelectListIterator<TSource, TResult> : Enumerable.Iterator<TResult>
{
    // Fields
    private List<TSource> source; // class has access to List source so can implement ICollection
    // code below snipped
}


public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, IList, ICollection, IEnumerable
{
public List(IEnumerable<T> collection)
{
    ICollection<T> is2 = collection as ICollection<T>;
    if (is2 != null)
    {
        int count = is2.Count;
        this._items = new T[count];
        is2.CopyTo(this._items, 0); // FAST
        this._size = count;
    }
    else
    {
        this._size = 0;
        this._items = new T[4];
        using (IEnumerator<T> enumerator = collection.GetEnumerator())
        {
            while (enumerator.MoveNext())
            {
                this.Add(enumerator.Current);  // SLOW, CAUSES ARRAY EXPANSION
            }
        }
    }
}

我已经对此进行了测试,结果证实了我的怀疑:

ICollection:2388.5222 毫秒

IEnumerable:3308.3382 毫秒

测试代码如下:

    // prepare source
    var n = 10000;
    var source = new List<int>(n);
    for (int i = 0; i < n; i++) source.Add(i);

    // Test List creation using ICollection
    var startTime = DateTime.Now;
    for (int i = 0; i < n; i++)
    {
        foreach(int l in source.Select(k => k)); // itterate to make comparison fair
        new List<int>(source);
    }
    var finishTime = DateTime.Now;
    Response.Write("ICollection: " + (finishTime - startTime).TotalMilliseconds + " ms <br />");

    // Test List creation using IEnumerable
    startTime = DateTime.Now;
    for (int i = 0; i < n; i++) new List<int>(source.Select(k => k));
    finishTime = DateTime.Now;
    Response.Write("IEnumerable: " + (finishTime - startTime).TotalMilliseconds + " ms");

我是否遗漏了什么,或者这会在未来的框架版本中得到修复吗?

谢谢你的想法。

最佳答案

LINQ to Objects 使用一些技巧来优化某些操作。例如,如果您链​​接两个 .Where语句在一起,谓词将组合成一个 WhereArrayIterator ,所以之前的可以被垃圾回收。同样,一个 Where其次是 Select将创建一个 WhereSelectArrayIterator ,将组合谓词作为参数传递,以便原始 WhereArrayiterator可以被垃圾收集。所以 WhereSelectArrayIterator不仅负责跟踪 selector , 也是组合 predicate它可能基于也可能不基于。

source字段只跟踪给出的初始列表。由于谓词,迭代结果将不会始终具有与 source 相同的项目数。做。由于 LINQ 旨在进行延迟评估,因此不应评估 source。针对 predicate提前只是为了如果有人最终调用.Count()可能会节省时间.这将导致与调用 .ToList() 一样多的性能损失。手动在上面,如果用户通过多个 Where 运行它和 Select子句,你最终会不必要地构建多个列表。

能否重构 LINQ to Objects 以创建 SelectArrayIterator它在 Select 时使用直接在数组上调用?当然。它会提高性能吗?一点点。费用是多少?更少的代码重用意味着需要额外的代码来维护和测试。

因此我们找到了绝大多数“为什么语言/平台 X 没有功能 Y”问题的症结:每个功能和优化都有一些相关的成本,甚至微软也没有无限的资源.就像那里的所有其他公司一样,他们进行判断调用以确定执行 Select 的代码的运行频率。在数组上然后调用 .ToList()在它上面,以及是否值得在 LINQ 包中编写和维护另一个类来使其运行得更快一些。

关于c# - 为什么 WhereSelectArrayIterator 不实现 ICollection?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7082725/

相关文章:

javascript - 加速我的 Javascript 滚动事件代码

python - 我可以使用对象(类的实例)作为 Python 中的字典键吗?

c# - 我需要哪些软件才能精通 Microsoft 认可的语言?

c# - 在某些条件下创建列表的子列表 C#

c# - hibernate ;控制何时保存每个请求的 session

r - 查找两个列表中的常见单词

java - 使用方法来比较列表

c# - 二维数组不是IEnumerable吗?

ArrayList 中可能匹配项的 Java 迭代

android - 在android应用程序中,在android Manifest文件中给coreApp ="true"有什么用?为什么在 Honey Comb 之前不需要它?