问题是关于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;
}
}
答案 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。