我正在阅读一个问题 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
属性(property):
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() 时使用中间缓冲区?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17173215/