Unity3D中不使用unsafe与fixed关键字向字节数组写入值的高效方案
这个问题在Unity开发里挺常见的——毕竟unsafe代码在很多项目里是被禁用的,尤其是要发布到某些平台的时候。你提到的BinaryReader/BinaryWriter确实能用,但咱们得聊聊更高效的方案,毕竟Unity对GC和性能挺敏感的:
先说说BinaryReader/BinaryWriter的可行性
它们确实可以实现需求,但不是性能最优的选择。因为这两个类依赖Stream接口,你需要把字节数组包装成MemoryStream来使用,会产生额外的对象分配和调用开销。如果只是偶尔写入单个值,倒也能用,但高频场景下会增加GC压力。示例代码如下:
int value = 3; byte[] array = new byte[100]; int offset = 10; using (var ms = new MemoryStream(array, offset, sizeof(int))) using (var bw = new BinaryWriter(ms)) { bw.Write(value); }
更高效的替代方案
1. 手动位运算(零分配,极致性能,兼容所有Unity版本)
直接通过位运算拆分int的每个字节,写入数组指定位置。这种方法完全没有额外内存分配,性能和unsafe代码几乎一致,适合对性能要求极高的场景(比如帧循环里的高频数据处理)。
int value = 3; byte[] array = new byte[100]; int offset = 10; // 小端模式(x86/x64系统默认,Unity绝大多数场景用这个) array[offset] = (byte)(value & 0xFF); array[offset + 1] = (byte)((value >> 8) & 0xFF); array[offset + 2] = (byte)((value >> 16) & 0xFF); array[offset + 3] = (byte)((value >> 24) & 0xFF); // 如果需要大端模式(比如网络协议场景),调整字节顺序即可 // array[offset + 3] = (byte)(value & 0xFF); // array[offset + 2] = (byte)((value >> 8) & 0xFF); // array[offset + 1] = (byte)((value >> 16) & 0xFF); // array[offset] = (byte)((value >> 24) & 0xFF);
2. MemoryMarshal.Write(零分配,代码简洁,Unity 2020+支持)
如果你的Unity项目版本在2020及以上(引入了较新的.NET Runtime),MemoryMarshal.Write是最优解——它通过Span<T>直接操作数组内存,既没有unsafe代码,也没有额外分配,代码还很简洁。
int value = 3; byte[] array = new byte[100]; int offset = 10; // 获取从offset开始、长度为int字节数的Span<byte> Span<byte> targetSpan = array.AsSpan(offset, sizeof(int)); // 直接将int值写入Span对应的内存区域 MemoryMarshal.Write(targetSpan, ref value);
这个方法内部逻辑和unsafe的内存操作一致,但完全符合Unity的代码规范,没有任何安全风险。
3. BitConverter + Buffer.BlockCopy(代码简单,适合低频场景)
BitConverter.GetBytes可以快速把int转成byte数组,再用Buffer.BlockCopy复制到目标数组的指定位置。缺点是BitConverter.GetBytes会每次分配一个4字节的数组,高频使用会产生GC垃圾。如果要优化,可以提前缓存一个byte数组重复使用。
普通版(有分配):
int value = 3; byte[] array = new byte[100]; int offset = 10; byte[] intBytes = BitConverter.GetBytes(value); Buffer.BlockCopy(intBytes, 0, array, offset, intBytes.Length);
优化版(复用缓存,减少分配):
// 提前在类里初始化缓存数组,全局复用 private static readonly byte[] _intCacheBuffer = new byte[4]; // 使用时 int value = 3; byte[] array = new byte[100]; int offset = 10; // 先把int值写入缓存(这里可以用手动位运算避免BitConverter的分配) _intCacheBuffer[0] = (byte)(value & 0xFF); _intCacheBuffer[1] = (byte)((value >> 8) & 0xFF); _intCacheBuffer[2] = (byte)((value >> 16) & 0xFF); _intCacheBuffer[3] = (byte)((value >> 24) & 0xFF); // 再复制到目标数组 Buffer.BlockCopy(_intCacheBuffer, 0, array, offset, _intCacheBuffer.Length);
总结选择建议
- 追求极致性能且要兼容所有Unity版本:选手动位运算
- Unity版本≥2020,想要代码简洁又高效:选MemoryMarshal.Write
- 低频使用、代码简单优先:选BitConverter + Buffer.BlockCopy
- 已经在使用BinaryReader/BinaryWriter处理序列化逻辑:可以顺便用它们,但单独写单个值不推荐
内容的提问来源于stack exchange,提问作者Hyunjik Bae

