使用`ToArray`方法复制数组

时间:2013-11-13 21:19:41

标签: c# performance linq

好吧,也许我只是懒惰,但这对于互联网来说可能是一个很酷的问题。

我知道Buffer.BlockCopy(...)在使用Array.Copy(...)时比byte[]更快。我准备写一个CloneBuffer帮助器,它将创建一个与源数组大小相同的数组,然后当我改为写时,使用Buffer.BlockCopy(...)将源数组复制到其中:

public void Send(byte[] data) {
    // Copy caller-provided buffer
    var buf = data.ToArray();

    // Start async send here and return immediately
}

是否有人知道ToArray()方法特别适用于byte[],或者这是否会慢于BlockCopy

2 个答案:

答案 0 :(得分:6)

您可以使用反射器程序查看Microsoft .NET程序集,例如ILSpy

这告诉我System.Linq.Enumerable::ToArray()的实现是:

public static TSource[] ToArray<TSource>(this IEnumerable<TSource> source)
{
    // ...
    return new Buffer<TSource>(source).ToArray();
}

内部结构Buffer<T>的构造函数:

  • 如果源可枚举实现ICollection<T>,则:
    • 分配Count元素数组,
    • 使用CopyTo()将集合复制到数组中。
  • 否则:
    • 分配一个包含4个元素的数组,
    • 开始枚举IEnumerable,将每个值存储在数组中。
    • 阵列太小了吗?
      • 创建一个大小为旧数组的新数组
      • 并将旧数组的内容复制到新数据中
      • 然后使用新数组,然后继续。

如果内部数组的大小与其中的元素数相匹配,则Buffer<T>.ToArray()只返回内部数组;否则将内部数组复制到具有确切大小的新数组。

请注意,此Buffer<T>类是内部的,与您提到的Buffer类无关。

所有复制均使用Array.Copy()完成。

因此,总结一下:所有复制都是使用Array.Copy()完成的,并且字节数组没有优化。但我不知道它是否比Buffer.BlockCopy()慢。要知道的唯一方法就是衡量。

答案 1 :(得分:0)

是的,它会变慢。

当您查看documentation for the System.Array methods时,System.Array.ToArray()没有定义。事实上,在查看继承/接口树之前,我们必须在找到此方法之前一直返回[IEnumerable.ToArray()][2]。由于这是仅使用IEnumerable的功能实现的,因此在开始执行时无法知道结果数组的大小。相反,它使用加倍算法在运行时构建数组。因此,您可能最终在制作副本的过程中创建并丢弃多个数组,并在销毁/重新创建每个中间缓冲区的过程中多次复制这些初始项。

如果你想要一个更简单,更天真的实现,至少要看Array.CopyTo()。记住:我说,“如果”。