You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求通过反射修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 09:02:16