如何访问私人列表<T> 成员?

How can I access private List<T> members?

一般来说,在C#中使用List比使用T[]更方便。然而,有时探查器显示 List 与 Array.Copy 和 Buffer.BlockCopy 等本机实现的批量操作相比具有显着的性能损失。此外,无法获取指向 List<> 元素的指针。

这使得在 Unity 中使用动态网格有些痛苦。如果我们可以访问 T[] List._items,则可以缓解其中一些问题。这可以在没有大量开销的情况下完成吗? (CPU 或垃圾)

使用反射总是可行的。这会为调用 GetValue() 生成几百字节的垃圾。它也不是很快;在 40 次 List< T > 访问的顺序上。

  // Helper class for fetching and caching FieldInfo values
  class FieldLookup {
    string sm_name;
    Dictionary<Type, FieldInfo> sm_cache;
    public FieldLookup(string name) {
      sm_name = name;
      sm_cache = new Dictionary<Type, FieldInfo>();
    }
    public FieldInfo Get(Type t) {
      try {
        return sm_cache[t];
      } catch (KeyNotFoundException) {
        var field = sm_cache[t] = t.GetField(
          sm_name,
          System.Reflection.BindingFlags.NonPublic |
          System.Reflection.BindingFlags.GetField |
          System.Reflection.BindingFlags.Instance);
        return field;
      }
    }
  }

  static FieldLookup sm_items = new FieldLookup("_items");

  public static T[] GetBackingArray<T>(this List<T> list) {
    return (T[])sm_items.Get(typeof(List<T>)).GetValue(list);
  }

如果您知道 List 的布局,那么您可以使用卑鄙的技巧来强制转换托管对象引用。不要使用它,除非你愿意在你 运行 所在的每个目标平台上进行测试,并在每次 Unity 升级时重新测试。

最危险的是它破坏了对象的运行时间和编译类型的不变量。编译器会为类型为TTo的对象生成代码,但是对象的RTTI字段仍然会显示类型为TFrom的对象。

  [StructLayout(LayoutKind.Explicit)]
  public struct ConvertHelper<TFrom, TTo>
      where TFrom : class
      where TTo : class {
    [FieldOffset( 0)] public long before;
    [FieldOffset( 8)] public TFrom input;
    [FieldOffset(16)] public TTo output;

    static public TTo Convert(TFrom thing) {
      var helper = new ConvertHelper<TFrom, TTo> { input = thing };
      unsafe {
        long* dangerous = &helper.before;
        dangerous[2] = dangerous[1];  // ie, output = input
      }
      var ret = helper.output;
      helper.input = null;
      helper.output = null;
      return ret;
    }
  }

  class PublicList<T> {
    public T[] _items;
  }

  public static T[] GetBackingArray<T>(this List<T> list) {
    return ConvertHelper<List<T>, PublicList<T>>.Convert(list)._items;
  }