调用有时Array.Copy比一个循环更有效吗?

时间:2014-01-15 18:07:31

标签: c# collections

问题是关于List的构造函数

public List(IEnumerable<T> collection)

此构造函数的代码

    /// <summary>
    /// Initializes a new instance of the <see cref="T:System.Collections.Generic.List`1"/> class that contains elements copied from the specified collection and has sufficient capacity to accommodate the number of elements copied.
    /// </summary>
    /// <param name="collection">The collection whose elements are copied to the new list.</param><exception cref="T:System.ArgumentNullException"><paramref name="collection"/> is null.</exception>
    [__DynamicallyInvokable]
    public List(IEnumerable<T> collection)
    {
      if (collection == null)
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
      ICollection<T> collection1 = collection as ICollection<T>;
      if (collection1 != null)
      {
        int count = collection1.Count;
        if (count == 0)
        {
          this._items = List<T>._emptyArray;
        }
        else
        {
          this._items = new T[count];
          collection1.CopyTo(this._items, 0);
          this._size = count;
        }
      }
      else
      {
        this._size = 0;
        this._items = List<T>._emptyArray;
        foreach (T obj in collection)
          this.Add(obj);
      }
    }

我很好奇收集不是ICollection的情况。在这种情况下,通过集合迭代迭代并计算元素的数量,使用List的Add方法。每次添加列表容量不足时,由Array.Copy进行复制。它发生在添加第5,第9,第17等元素之前(自4以来的2的倍数)。 我气馁了。有时调用Array.Copy比一个循环然后一个复制更有效吗?

下面我还添加了Add方法和相关方法EnsureCapacity和List Capacity字段

的代码
    public void Add(T item)
    {
      if (this._size == this._items.Length)
        this.EnsureCapacity(this._size + 1);
      this._items[this._size++] = item;
      ++this._version;
    }

    private void EnsureCapacity(int min)
    {
      if (this._items.Length >= min)
        return;
      int num = this._items.Length == 0 ? 4 : this._items.Length * 2;
      if ((uint) num > 2146435071U)
        num = 2146435071;
      if (num < min)
        num = min;
      this.Capacity = num;
    }

    public int Capacity
    {
      [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries"), __DynamicallyInvokable] get
      {
        return this._items.Length;
      }
      [__DynamicallyInvokable] set
      {
        if (value < this._size)
          ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value, ExceptionResource.ArgumentOutOfRange_SmallCapacity);
        if (value == this._items.Length)
          return;
        if (value > 0)
        {
          T[] objArray = new T[value];
          if (this._size > 0)
            Array.Copy((Array) this._items, 0, (Array) objArray, 0, this._size);
          this._items = objArray;
        }
        else
          this._items = List<T>._emptyArray;
      }
    }

1 个答案:

答案 0 :(得分:3)

这与效率无关。

由于IEnumerable<T>的性质未知(在这种情况下它不是ICollection<T>实现),因此foreach是从{{1}执行复制项目的唯一安全方法转到IEnumerable<T>

List<T>

static IEnumerable<string> GetSomeData() { using (var connection = new SqlConnection("...")) { connection.Open(); using (var command = new SqlCommand("select name from some_table", connection)) using (var reader = command.ExecuteReader()) { yield return reader.GetString(0); } } } 只能迭代一次,因为GetSomeData构造函数使用List<T>,所以它可以正常工作。根据您的修改,客户端代码将面临此行的异常:

Add

请注意, var list = new List<string>(GetSomeData()); 的一次性实施是正常的。来自MSDN

  

为COM互操作性提供了Reset方法。它不是   必须要实施;相反,实施者可以   只需抛出一个NotSupportedException。