2014-01-15 75 views
1

問題是關於表的構造有時會調用Array.Copy比一個循環更有效嗎?

此構造

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

public List(IEnumerable<T> collection) 

代碼。在這種情況下,迭代通過收集並計算元素的計數,使用List的Add方法。在每次添加列表的容量不足時,Array.Copy會發生複製。它在添加第5,第9,第17等元素(自4的倍數2)之前發生。 我很沮喪。有時候調用Array.Copy比一個循環更有效,然後複製一次?

而且我下面添加Add方法和相關方法的ensureCapacity和清單容量

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

該序列可以是一次性枚舉。想象一下包裝數據閱讀器的迭代器方法。 – Dennis

+0

在這種情況下,一次枚舉在我看來是錯誤的實現IEnumerable,因爲我們無法使用包含在接口中的Reset()方法。 –

+1

你錯了。 「Reset方法提供了COM互操作性,不一定需要實現;相反,實現者可以簡單地拋出一個NotSupportedException異常。」 – Dennis

回答

3

這不是效率的領域的代碼。

由於IEnumerable<T>性質不明(這是不是在這種情況下ICollection<T>實現),比foreach是從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());    

注意的是IEnumerable<T>一次性實施是很正常的事情。從MSDN

Reset方法提供COM互操作性。它不一定需要執行;相反,實施者可以簡單地拋出一個NotSupportedException。

+0

謝謝你的回答,我沒有'我知道這一點,當我看到ToList方法的實現時,我感到很驚訝。 –