求通过反射修改C#数组Length属性适配Unity SetIndices的方案
解决方案:无需复制数组修改C#数组Length(适配Unity SetIndices)
嘿,这个需求我之前在Unity开发里碰到过类似的,确实有点棘手——既要避免数组复制带来的GC开销,又要“骗”SetIndices只用数组的前一部分。下面给你几个可行的路子,从最安全的官方方案到应急的hack都有:
优先选:用Unity官方提供的SetIndices重载(完全无hack)
其实你可能忽略了,Unity的Mesh.SetIndices是有带起始索引和长度参数的重载的!根本不用修改数组Length,直接告诉引擎只用数组的某一段就行:
// 假设你有一个完整的索引数组fullIndices,想只用前newLength个元素 mesh.SetIndices(fullIndices, 0, newLength, MeshTopology.Triangles, 0);
这个方案是最安全的:没有GC,不依赖内部实现,官方完全支持,也不会有数组越界的风险。如果你的Unity版本支持这个重载(绝大多数现代版本都支持),这绝对是首选。
应急方案1:用不安全代码(Unsafe)直接修改Length字段
如果因为某些原因必须修改数组Length(比如你用的API确实只能接受数组Length作为判断依据),可以用Unsafe代码直接操作数组的内部字段——C#数组的Length其实是一个私有字段,在Unity的Mono环境下可以通过指针访问:
using System; using UnityEngine; unsafe void TruncateArrayLength<T>(T[] array, int newLength) { if (newLength < 0 || newLength > array.Length) throw new ArgumentOutOfRangeException(nameof(newLength)); fixed (void* arrayPtr = array) { // 在Unity Mono的64位环境下,数组对象头占16字节(8字节同步块 + 8字节类型句柄) // 跳过对象头后,第一个字段就是Length int* lengthFieldPtr = (int*)((byte*)arrayPtr + IntPtr.Size * 2); *lengthFieldPtr = newLength; } } // 使用示例: int[] indices = new int[1000]; // 填充数据... TruncateArrayLength(indices, 500); // 把Length改成500,实际内存还是1000的大小 mesh.SetIndices(indices, MeshTopology.Triangles, 0);
注意事项:
- 必须在Unity的PlayerSettings里开启Allow Unsafe Code(在Player Settings > Other Settings > Configuration里);
- 这个写法依赖Mono的数组内部布局,如果你切换到IL2CPP编译,可能会失效(IL2CPP的数组结构不同);
- 修改后数组的实际内存还是原大小,只是Length被修改了——如果之后不小心访问超过newLength的元素,会直接触发数组越界异常,要自己做好边界检查;
- 不会触发GC,因为没有创建任何新对象。
应急方案2:用反射修改私有Length字段
如果你不想用Unsafe代码,也可以用反射来修改数组的私有m_length字段:
using System; using System.Reflection; using UnityEngine; void TruncateArrayLengthViaReflection<T>(T[] array, int newLength) { if (newLength < 0 || newLength > array.Length) throw new ArgumentOutOfRangeException(nameof(newLength)); // 获取Array类的私有m_length字段 FieldInfo lengthField = typeof(Array).GetField("m_length", BindingFlags.NonPublic | BindingFlags.Instance); lengthField.SetValue(array, newLength); } // 使用示例: int[] indices = new int[1000]; TruncateArrayLengthViaReflection(indices, 500); mesh.SetIndices(indices, MeshTopology.Triangles, 0);
注意事项:
- 反射的开销比Unsafe代码略大,但比复制数组小得多;
- 同样依赖.NET/Mono的内部字段命名(
m_length),IL2CPP环境下可能无法识别; - 同样要注意数组越界的问题,实际内存还是原大小。
总结
- 优先用官方的
SetIndices重载,这是最稳妥的方案; - 如果必须修改数组Length,Unsafe代码是性能最好的选择,但要注意平台兼容性;
- 反射是更“安全”的hack(不用开启Unsafe),但有轻微性能开销。
内容的提问来源于stack exchange,提问作者Vili Volcini
相关产品推荐
相关产品推荐

