如何将Primitive[]转换为byte[]

4

对于原始数组的序列化,我想知道如何将Primitive[]转换为其相应的byte[](例如,将int [128]转换为byte [512],或将ushort []转换为byte [] ...)。

目标可以是内存流,网络消息,文件等任何内容。目标是实现性能(序列化和反序列化时间),以便能够使用某些流一次性地写入byte[],而不是循环所有值,或使用某些转换器分配。

已经探索的一些解决方案:

常规循环进行写入/读取

//array = any int[];
myStreamWriter.WriteInt32(array.Length);
for(int i = 0; i < array.Length; ++i)
   myStreamWriter.WriteInt32(array[i]);

这个解决方案适用于序列化和反序列化,并且比使用标准的System.Runtime.Serialization与BinaryFormater结合来序列化/反序列化单个int或一些int要快100倍。

但是,如果数组长度(对于Int32)超过200/300个值,则此解决方案会变慢。

强制类型转换?

C#似乎不能直接将Int[]转换为byte[]或bool[]转换为byte[]。

BitConverter.GetBytes()

此解决方案有效,但它在循环遍历我的int[]时每次都分配一个新的byte[]。性能当然很差。

Marshal.Copy

是的,这个解决方案也有效,但与前面的BitConverter相同,存在相同的问题。

C++ hack

由于C#不允许直接转换,所以在查看存储在数组数据之前4个字节的长度后,我尝试了一些C++ hack。

ARRAYCAST_API void Cast(int* input, unsigned char** output)
{
   // get the address of the input (this is a pointer to the data)
   int* count = input;
   // the size of the buffer is located just before the data (4 bytes before as this is an int)
   count--;
   // multiply the number of elements by 4 as an int is 4 bytes
   *count = *count * 4;
   // set the address of the byte array
   *output = (unsigned char*)(input);
}

并且调用C#:

byte[] arrayB = null;
int[] arrayI = new int[128];
for (int i = 0; i < 128; ++i)
   arrayI[i] = i;

// delegate call
fptr(arrayI, out arrayB);

我成功将我的int [128]获取到C ++中,切换数组长度并将正确的地址分配给我的“output”变量,但C#只返回一个byte [1]。看来我不能这么容易地修改托管变量。
因此,我真的开始认为所有我想要实现的这些类型转换在C#中都是不可能的(int []-> byte [],bool []-> byte [],double []-> byte [] ...)而不需要分配/复制...
我漏掉了什么?

你能更具体地说明你正在尝试做什么吗?你要序列化数组吗?序列化到哪里?硬盘驱动速度可能是真正的瓶颈。也许你应该使用byte[]来保存原始数据(不需要序列化,但检索数据比较棘手)。 - Sinatr
我很惊讶普通循环的性能如此糟糕。我认为它应该更好,仅仅循环两三百个值并不像是会导致性能问题的事情。也许你应该进一步调查这个问题,而不是将其视为不可避免的性能影响。 - Chris
关于字节序(endianness)怎么样呢?两端保证是一样的吗? - user4003407
@PetSerAl:在现代架构中,字节序几乎都是相同的。 - leppie
@Chris 当然我不能将 int[128] 转换为 byte[128]。我在这里谈论的是从 int[128] 到 byte[512] 的转换。 关于 MemoryStream,我已经将我的数组序列化到 MemoryStream 中,并从该 MemoryStream 进行反序列化。 - jlevet
显示剩余7条评论
1个回答

4

使用Buffer.BlockCopy如何?

// serialize
var intArray = new[] { 1, 2, 3, 4, 5, 6, 7, 8 };
var byteArray = new byte[intArray.Length * 4];
Buffer.BlockCopy(intArray, 0, byteArray, 0, byteArray.Length);

// deserialize and test
var intArray2 = new int[byteArray.Length / 4];
Buffer.BlockCopy(byteArray, 0, intArray2, 0, byteArray.Length);
Console.WriteLine(intArray.SequenceEqual(intArray2));    // true

请注意,BlockCopy仍在幕后分配/复制。我相当确定这在托管代码中是不可避免的,BlockCopy可能是最好的选择。

BlockCopy对于原始值类型数组有效吗?(我猜是的) - leppie
那可能是一个不错的方法。我会尝试这个方法,使用某种静态锁定的byte[]缓冲区来避免多次分配并保持线程安全。谢谢你的想法。 - jlevet
我正在使用这种方法,除了十进制类型。 - jlevet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接