C#不安全值类型数组到字节数组转换

我使用扩展方法将float数组转换为字节数组:

public static unsafe byte[] ToByteArray(this float[] floatArray, int count) { int arrayLength = floatArray.Length > count ? count : floatArray.Length; byte[] byteArray = new byte[4 * arrayLength]; fixed (float* floatPointer = floatArray) { fixed (byte* bytePointer = byteArray) { float* read = floatPointer; float* write = (float*)bytePointer; for (int i = 0; i < arrayLength; i++) { *write++ = *read++; } } } return byteArray; } 

我知道数组是指向与元素类型和数量信息相关的内存的指针。 此外,在我看来,如果没有像上面那样复制数据,就没有办法从字节数组转换到字节数组。

我明白了吗? 甚至不可能编写IL来从指针,类型和长度创建数组而不复制数据?

编辑:谢谢你的答案,我学到了一些基础知识,并尝试了新的技巧!

在最初接受Davy Landman的回答之后,我发现虽然他出色的StructLayout黑客确实将字节数组转换为浮点数组,但它却无法正常工作。 展示:

 [StructLayout(LayoutKind.Explicit)] struct UnionArray { [FieldOffset(0)] public Byte[] Bytes; [FieldOffset(0)] public float[] Floats; } static void Main(string[] args) { // From bytes to floats - works byte[] bytes = { 0, 1, 2, 4, 8, 16, 32, 64 }; UnionArray arry = new UnionArray { Bytes = bytes }; for (int i = 0; i < arry.Bytes.Length / 4; i++) Console.WriteLine(arry.Floats[i]); // From floats to bytes - index out of range float[] floats = { 0.1f, 0.2f, 0.3f }; arry = new UnionArray { Floats = floats }; for (int i = 0; i < arry.Floats.Length * 4; i++) Console.WriteLine(arry.Bytes[i]); } 

似乎CLR将两个arrays视为具有相同的长度。 如果结构是从float数据创建的,则字节数组的长度太短。

是的,类型信息和数据在同一个内存块中,因此除非你覆盖浮点数组中的类型信息以欺骗系统它的字节数组,否则这是不可能的。 那将是一个非常难看的黑客,并且可能很容易爆炸……

如果您愿意,可以使用以下方法在没有不安全代码的情况下转换浮点数:

 public static byte[] ToByteArray(this float[] floatArray) { int len = floatArray.Length * 4; byte[] byteArray = new byte[len]; int pos = 0; foreach (float f in floatArray) { byte[] data = BitConverter.GetBytes(f); Array.Copy(data, 0, byteArray, pos, 4); pos += 4; } return byteArray; } 

你可以使用一个非常丑陋的黑客来使用内存操作临时将你的数组更改为byte []。

这非常快速有效,因为它不需要克隆数据并对其进行迭代。

我在32位和64位操作系统中测试了这个hack,所以它应该是可移植的。

源代码+样本使用情况保留在https://gist.github.com/1050703 ,但为方便起见,我也将其粘贴在此处:

 public static unsafe class FastArraySerializer { [StructLayout(LayoutKind.Explicit)] private struct Union { [FieldOffset(0)] public byte[] bytes; [FieldOffset(0)] public float[] floats; } [StructLayout(LayoutKind.Sequential, Pack = 1)] private struct ArrayHeader { public UIntPtr type; public UIntPtr length; } private static readonly UIntPtr BYTE_ARRAY_TYPE; private static readonly UIntPtr FLOAT_ARRAY_TYPE; static FastArraySerializer() { fixed (void* pBytes = new byte[1]) fixed (void* pFloats = new float[1]) { BYTE_ARRAY_TYPE = getHeader(pBytes)->type; FLOAT_ARRAY_TYPE = getHeader(pFloats)->type; } } public static void AsByteArray(this float[] floats, Action action) { if (floats.handleNullOrEmptyArray(action)) return; var union = new Union {floats = floats}; union.floats.toByteArray(); try { action(union.bytes); } finally { union.bytes.toFloatArray(); } } public static void AsFloatArray(this byte[] bytes, Action action) { if (bytes.handleNullOrEmptyArray(action)) return; var union = new Union {bytes = bytes}; union.bytes.toFloatArray(); try { action(union.floats); } finally { union.floats.toByteArray(); } } public static bool handleNullOrEmptyArray(this TSrc[] array, Action action) { if (array == null) { action(null); return true; } if (array.Length == 0) { action(new TDst[0]); return true; } return false; } private static ArrayHeader* getHeader(void* pBytes) { return (ArrayHeader*)pBytes - 1; } private static void toFloatArray(this byte[] bytes) { fixed (void* pArray = bytes) { var pHeader = getHeader(pArray); pHeader->type = FLOAT_ARRAY_TYPE; pHeader->length = (UIntPtr)(bytes.Length / sizeof(float)); } } private static void toByteArray(this float[] floats) { fixed(void* pArray = floats) { var pHeader = getHeader(pArray); pHeader->type = BYTE_ARRAY_TYPE; pHeader->length = (UIntPtr)(floats.Length * sizeof(float)); } } } 

用法是:

 var floats = new float[] {0, 1, 0, 1}; floats.AsByteArray(bytes => { foreach (var b in bytes) { Console.WriteLine(b); } }); 

这个问题与将float []转换为byte []的最快方法相反? 。

我已经用一种联盟的方式回答了跳过整个数据的复制。 您可以轻松地将其反转(长度=长度* sizeof(双精度)。

我已经写了类似的东西,以便在数组之间快速转换。 它基本上是一个丑陋的概念validation,而不是一个英俊的解决方案。 ;)

 public static TDest[] ConvertArray(TSource[] source) where TSource : struct where TDest : struct { if (source == null) throw new ArgumentNullException("source"); var sourceType = typeof(TSource); var destType = typeof(TDest); if (sourceType == typeof(char) || destType == typeof(char)) throw new NotSupportedException( "Can not convert from/to a char array. Char is special " + "in a somewhat unknown way (like enums can't be based on " + "char either), and Marshal.SizeOf returns 1 even when the " + "values held by a char can be above 255." ); var sourceByteSize = Buffer.ByteLength(source); var destTypeSize = Marshal.SizeOf(destType); if (sourceByteSize % destTypeSize != 0) throw new Exception( "The source array is " + sourceByteSize + " bytes, which can " + "not be transfered to chunks of " + destTypeSize + ", the size " + "of type " + typeof(TDest).Name + ". Change destination type or " + "pad the source array with additional values." ); var destCount = sourceByteSize / destTypeSize; var destArray = new TDest[destCount]; Buffer.BlockCopy(source, 0, destArray, 0, sourceByteSize); return destArray; } } 
  public byte[] ToByteArray(object o) { int size = Marshal.SizeOf(o); byte[] buffer = new byte[size]; IntPtr p = Marshal.AllocHGlobal(size); try { Marshal.StructureToPtr(o, p, false); Marshal.Copy(p, buffer, 0, size); } finally { Marshal.FreeHGlobal(p); } return buffer; } 

这可能有助于您将对象转换为字节数组。

您应该检查我对类似问题的回答: 将float []转换为byte []的最快方法是什么? 。

在其中,您将找到可移植代码(32/64位兼容),以便您可以将浮点数组视为字节数组,反之亦然,而无需复制数据。 这是我知道做这种事情的最快方式。

如果您只对代码感兴趣,请将其维护在https://gist.github.com/1050703 。

好吧 – 如果你仍然对那个hack感兴趣 – 看看这个修改过的代码 – 它就像一个魅力并且花费大约0时间,但它可能在将来不起作用,因为它是一个hack,允许获得对整个进程地址空间的完全访问权限信任要求和不安全标记。

  [StructLayout(LayoutKind.Explicit)] struct ArrayConvert { public static byte[] GetBytes(float[] floats) { ArrayConvert ar = new ArrayConvert(); ar.floats = floats; ar.length.val = floats.Length * 4; return ar.bytes; } public static float[] GetFloats(byte[] bytes) { ArrayConvert ar = new ArrayConvert(); ar.bytes = bytes; ar.length.val = bytes.Length / 4; return ar.floats; } public static byte[] GetTop4BytesFrom(object obj) { ArrayConvert ar = new ArrayConvert(); ar.obj = obj; return new byte[] { ar.top4bytes.b0, ar.top4bytes.b1, ar.top4bytes.b2, ar.top4bytes.b3 }; } public static byte[] GetBytesFrom(object obj, int size) { ArrayConvert ar = new ArrayConvert(); ar.obj = obj; ar.length.val = size; return ar.bytes; } class ArrayLength { public int val; } class Top4Bytes { public byte b0; public byte b1; public byte b2; public byte b3; } [FieldOffset(0)] private Byte[] bytes; [FieldOffset(0)] private object obj; [FieldOffset(0)] private float[] floats; [FieldOffset(0)] private ArrayLength length; [FieldOffset(0)] private Top4Bytes top4bytes; }