这是一个两遍算法吗?即,它迭代可枚举一次以计算元素的数量,以便它可以分配数组,然后再次传递以插入它们?
它是否循环一次,并继续调整数组的大小?
或者它是否使用像List这样的中间结构(可能在内部调整数组大小)?
答案 0 :(得分:17)
它使用中间结构。涉及的实际类型是Buffer,它是框架中的内部结构。实际上,这种类型有一个数组,每次完整时都会复制它以分配更多空间。这个数组的长度为4(在.NET 4中,它是一个可能会改变的实现细节),因此在执行ToArray时可能最终会分配和复制很多。
但是有一个优化。如果源实现ICollection<T>
,它使用Count来从头开始分配正确大小的数组。
答案 1 :(得分:10)
首先检查来源是否为ICollection<T>
,在这种情况下,它可以调用来源的ToArray()
方法。
否则,它只列举一次源。因为它枚举它将项目存储到缓冲区数组中。每当它到达缓冲区数组的末尾时,它就会创建一个大小为两倍的新缓冲区并复制旧元素。枚举完成后,它返回缓冲区(如果它的大小正确)或将项目从缓冲区复制到一个大小合适的数组中。
这是操作的伪源代码:
public static T[] ToArray<T>(this IEnumerable<T> source)
{
T[] items = null;
int count = 0;
foreach (T item in source)
{
if (items == null)
{
items = new T[4];
}
else if (items.Length == count)
{
T[] destinationArray = new T[count * 2];
Array.Copy(items, 0, destinationArray, 0, count);
items = destinationArray;
}
items[count] = item;
count++;
}
if (items.Length == count)
{
return items;
}
T[] destinationArray = new TElement[count];
Array.Copy(items, 0, destinationArray, 0, count);
return destinationArray;
}
答案 2 :(得分:6)
像这样(通过.NET Reflector):
public static TSource[] ToArray<TSource>(this IEnumerable<TSource> source)
{
if (source == null)
{
throw Error.ArgumentNull("source");
}
Buffer<TSource> buffer = new Buffer<TSource>(source);
return buffer.ToArray();
}
[StructLayout(LayoutKind.Sequential)]
internal struct Buffer<TElement>
{
internal TElement[] items;
internal int count;
internal Buffer(IEnumerable<TElement> source)
{
TElement[] array = null;
int length = 0;
ICollection<TElement> is2 = source as ICollection<TElement>;
if (is2 != null)
{
length = is2.Count;
if (length > 0)
{
array = new TElement[length];
is2.CopyTo(array, 0);
}
}
else
{
foreach (TElement local in source)
{
if (array == null)
{
array = new TElement[4];
}
else if (array.Length == length)
{
TElement[] destinationArray = new TElement[length * 2];
Array.Copy(array, 0, destinationArray, 0, length);
array = destinationArray;
}
array[length] = local;
length++;
}
}
this.items = array;
this.count = length;
}
internal TElement[] ToArray()
{
if (this.count == 0)
{
return new TElement[0];
}
if (this.items.Length == this.count)
{
return this.items;
}
TElement[] destinationArray = new TElement[this.count];
Array.Copy(this.items, 0, destinationArray, 0, this.count);
return destinationArray;
}
}
答案 3 :(得分:2)
首先,将项目加载到内部类Buffer<T>
中,以允许生成计数
接下来,调用Buffer<T>.ToArray
,将Array.Copy
的数组Buffer<T>
放入返回的数组中。
如果您想亲眼看看,.NET Reflector会显示此代码。
答案 4 :(得分:1)
通常,尝试迭代两次可枚举可能会导致灾难,因为无法保证可以再次迭代枚举。因此,执行Count
然后分配然后复制就不存在了。
在Reflector中,它显示它使用一个名为Buffer
的类型,它有效地将序列流式化为一个数组调整大小(每次重新分配加倍,以便重新分配的数量为O(log n)
),然后根据需要到达末尾时返回一个适当大小的数组