今天群友提出了以下问题
![]()
直接强制转换为Span<byte>
避免复制
在 C# 中,由于类型安全的设计,不能直接将 int[]
强制转换为 byte[]
。即使在 unsafe
代码块中,int[]
和 byte[]
也是不同的类型,内存布局也不同。
除非不用 byte[]
,而是用 Span<byte>
如果可以接受使用 Span<byte>
,则可以直接强制转换避免复制。
因为是直接访问不复制,所以默认会是本机的字节序,不考虑大小端问题。
也可以再此基础上使用 ToArray()
方法,把Span<byte>
转为 byte[]
(会有复制)。
使用 MemoryMarshal
和 Span<T>
使用 MemoryMarshal.Cast<TFrom, TTo>
方法,可以在不复制数据的情况下,将 int[]
转换为 Span<byte>
,直接访问其底层字节。
注意:
- 这种方法不复制数据,直接创建一个新的
Span<byte>
来表示原始数组的字节视图。 - 返回的是
Span<byte>
,不能直接转换为 byte[]
,但可以根据需要复制到 byte[]
中(直接调用.ToArray()方法)。
1 2 3 4 5 6 7 8 9 10 11 12 13
| using System; using System.Runtime.InteropServices;
public static class IntArrayExtensions { public static Span<byte> AsByteSpan(this int[] intArray) { if (intArray == null) throw new ArgumentNullException(nameof(intArray));
return MemoryMarshal.Cast<int, byte>(intArray); } }
|
使用 unsafe
和指针
在 unsafe
代码块中,可以使用指针直接访问数组的内存:
注意:
- 通过指针转换,直接获取
int[]
的字节视图。 - 需要在项目设置中启用不安全代码(Allow unsafe code)。
1 2 3 4 5 6 7 8 9 10 11
| public static unsafe Span<byte> AsByteSpanUnsafe(this int[] intArray) { if (intArray == null) throw new ArgumentNullException(nameof(intArray));
fixed (int* pIntArray = intArray) { byte* pByteArray = (byte*)pIntArray; return new Span<byte>(pByteArray, intArray.Length * sizeof(int)); } }
|
复制为 byte[]
因为是复制,所以增加对于字节序的考虑
使用 Buffer.BlockCopy
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public static byte[] ToByteArray(this int[] intArray, bool asLittleEndian = true) { if (intArray == null) throw new ArgumentNullException(nameof(intArray));
byte[] byteArray = new byte[intArray.Length * sizeof(int)];
if (BitConverter.IsLittleEndian == asLittleEndian) { Buffer.BlockCopy(intArray, 0, byteArray, 0, byteArray.Length); } else { for (int i = 0; i < intArray.Length; i++) { byte[] bytes = BitConverter.GetBytes(intArray[i]); Array.Reverse(bytes); Buffer.BlockCopy(bytes, 0, byteArray, i * sizeof(int), sizeof(int)); } }
return byteArray; }
|
使用Span
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public static byte[] ToByteArrayUseSpan(this int[] intArray, bool asLittleEndian = true) { if (intArray == null) throw new ArgumentNullException(nameof(intArray));
byte[] byteArray = new byte[intArray.Length * sizeof(int)]; Span<byte> byteSpan = byteArray.AsSpan();
for (int i = 0; i < intArray.Length; i++) { int value = intArray[i]; int offset = i * sizeof(int);
if (asLittleEndian) { BinaryPrimitives.WriteInt32LittleEndian(byteSpan.Slice(offset, sizeof(int)), value); } else { BinaryPrimitives.WriteInt32BigEndian(byteSpan.Slice(offset, sizeof(int)), value); } }
return byteArray; }
|
使用 unsafe
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| public static unsafe byte[] ToByteArrayUnsafe(this int[] intArray, bool asLittleEndian = true) { if (intArray == null) throw new ArgumentNullException(nameof(intArray));
int length = intArray.Length * sizeof(int); byte[] byteArray = new byte[length];
fixed (int* pIntArray = intArray) fixed (byte* pByteArray = byteArray) { int* pInt = pIntArray; byte* pByte = pByteArray;
for (int i = 0; i < intArray.Length; i++, pInt++, pByte += sizeof(int)) { int value = *pInt;
if (BitConverter.IsLittleEndian != asLittleEndian) { value = SwapEndianness(value); }
*(int*)pByte = value; } }
return byteArray; }
private static int SwapEndianness(int value) { return ((value >> 24) & 0x000000FF) | ((value >> 8) & 0x0000FF00) | ((value << 8) & 0x00FF0000) | ((value << 24) & unchecked((int)0xFF000000)); }
|