.net - 为什么 Enumerable<T>.ToArray() 可以先调用 Count() 时使用中间缓冲区?

标签 .net performance linq

我正在阅读一个问题 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/

相关文章:

c# - MigraDoc:在横向和纵向页面上居中放置水印

mysql - 构建用户收藏夹表的正确方法是什么(性能)

php - 哪个更快 : to run the same sql query twice or run only once and store the result in arrays?

c# - 在集合中查找具有相同值的 block 并操作周围的值

c# - Linq 查询中的 "Property is not defined"运行时错误

c# - 使用 LINQ 按频率和值排序

c# - 如何生成指向不在 WebAPI 路由表中的资源的链接?

.net - 如何将 2 个参数传递给 Nant 脚本?

c#-4.0 - SecurityAction.RequestMinimum 在 .Net 4.0 中已过时

c++ - 快速加载文件格式的寄存器宽度和解析