我正在阅读一个询问 Is it better to call ToList() or ToArray() in LINQ queries? 的问题,发现自己想知道为什么 Enumerable.ToArray()
不会首先调用 Count()
方法来查找集合的大小,而不是使用动态调整自身大小的内部 Buffer{T}
类。类似于以下内容:
T[] ToArray<T>(IEnumerable<T> source)
{
var count = source.Count();
var array = new T[count];
int index = 0;
foreach (var item in source) array[index++] = item;
return array;
}
我知道我们无法理解设计师和实现者的想法,我相信他们比我更聪明。所以问这个问题的最好方法是上面显示的方法有什么问题?它似乎减少了内存分配,并且仍然在 O(n) 时间内运行。
最佳答案
首先,Buffer<T>
类构造函数也会优化指定的序列是否可以转换为具有 ICollection
属性的 Count
(如数组或列表):
TElement[] array = null;
int num = 0;
ICollection<TElement> collection = source as ICollection<TElement>;
if (collection != null)
{
num = collection.Count;
if (num > 0)
{
array = new TElement[num];
collection.CopyTo(array, 0);
}
}
else
// now we are going the long way ...
因此,如果它不是集合,则必须执行查询以获取总数。但是仅使用
Enumerable.Count
来初始化正确大小的数组可能非常昂贵,而且 - 更重要的是 - 可能会产生危险的副作用。因此它是不安全的。考虑这个简单的
File.ReadLines
示例:var lines = File.ReadLines(path);
int count = lines.Count(); // executes the query which also disposes the underlying IO.TextReader
var array = new string[count];
int index = 0;
foreach (string line in lines) array[index++] = line;
这将抛出
ObjectDisposedException
“无法从关闭的 TextReader 中读取”,因为 lines.Count()
已经执行了查询,同时读取器位于 foreach
处。关于.net - 为什么 Enumerable<T>.ToArray() 在它可以先调用 Count() 时使用中间 Buffer?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/17173215/