【群友笔记】int[] 如何强转为 byte[]

今天群友提出了以下问题

直接强制转换为Span<byte>避免复制

在 C# 中,由于类型安全的设计,不能直接将 int[] 强制转换为 byte[]。即使在 unsafe 代码块中,int[]byte[] 也是不同的类型,内存布局也不同。

除非不用 byte[],而是用 Span<byte>

如果可以接受使用 Span<byte>,则可以直接强制转换避免复制。
因为是直接访问不复制,所以默认会是本机的字节序,不考虑大小端问题。
也可以再此基础上使用 ToArray() 方法,把Span<byte> 转为 byte[](会有复制)。

使用 MemoryMarshalSpan<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));
}

欢迎关注我的其它发布渠道