问题
In general, in C# it is more convenient to use List than T[]. However, there are times when the profiler shows that List has significant performance penalties compared to natively-implemented bulk operations like Array.Copy and Buffer.BlockCopy. In addition, it is not possible to get pointers to List<> elements.
This makes working with dynamic meshes in Unity somewhat painful. Some of these problems could be alleviated if we could access T[] List._items. Is this possible to do without significant overhead? (either CPU or garbage)
回答1:
If you know the layout of List, then you can use a dirty trick to cast managed object references. Do not use this unless you're willing to test on every target platform you run on, and re-test with every Unity upgrade.
The most dangerous thing about this is that it breaks invariants about the runtime and compiled type of the object. The compiler will generate code for an object of type TTo, but the object's RTTI field will still show an object of type 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;
}
回答2:
Using reflection is always possible. This generates a few hundred bytes of garbage for the call to GetValue(). It is also not very fast; on the order of 40 List< T > accesses.
// 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);
}
来源:https://stackoverflow.com/questions/35588645/how-can-i-access-private-listt-members