I'm have a large list of value types that needs to be given to OpenGL. It would be great if this could happen as quickly as possible. What I'm doing now looks like this:
List<Vertex> VList = new List<Vertex>();
... //Add vertices
Vertex[] VArray;
VList.CopyTo(VArray, VList.Length);
GL.SetData(..., VArray);
This list is easily 10MB big, so copying is slow. Can I do this without copying, like somehow get a pointer to the array used internally by List?
Or do I have to implement my own List class..
EDIT: I forgot to mention that I don't know the number of elements that will be added to the List.
Definitely use a List<T> any time you want to add/remove data, since resizing arrays is expensive. If you know the data is fixed length, and you want to micro-optimise for some very specific reason (after benchmarking), then an array may be useful.
List<T> is a class, and all class instances are passed by reference.
ToList() makes a shallow copy. The references are copied, but the new references still point to the same instances as the original references point to.
If you need to access internal array repeatedly, it good practice to store accessor as delegate.
In this example, it's delegate to dynamic method. First call may not be fast, but subsequent calls (on List of same type) will be much faster.
public static class ListExtensions { static class ArrayAccessor<T> { public static Func<List<T>, T[]> Getter; static ArrayAccessor() { var dm = new DynamicMethod("get", MethodAttributes.Static | MethodAttributes.Public, CallingConventions.Standard, typeof(T[]), new Type[] { typeof(List<T>) }, typeof(ArrayAccessor<T>), true); var il = dm.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); // Load List<T> argument il.Emit(OpCodes.Ldfld, typeof(List<T>).GetField("_items", BindingFlags.NonPublic | BindingFlags.Instance)); // Replace argument by field il.Emit(OpCodes.Ret); // Return field Getter = (Func<List<T>, T[]>)dm.CreateDelegate(typeof(Func<List<T>, T[]>)); } } public static T[] GetInternalArray<T>(this List<T> list) { return ArrayAccessor<T>.Getter(list); } }
Make sure to include:
using System.Reflection; using System.Reflection.Emit;
I wouldn't recommend what you want to do. Why are you using a List<T>
in the first place? If you can tell us precisely what characteristics the data-structure that you want to create should have, and how it should interface with the consuming API, we might be able to give you a proper solution to your problem.
But I will try to answer the question as asked.
Can I do this without copying, like somehow get a pointer to the array used internally by List?
Yes, although you would be relying on an undocumented implementation detail. As of NET 4.0, the backing array field is called _items
.
Vertex[] vertices = (Vertex[]) typeof(List<Vertex>)
.GetField("_items", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(VList);
Do note that this array will almost certainly have slack at the end (that's the whole point of List<T>
), so array.Length
on this array won't be all that useful. The API that consumes the array would need to be notified of the "real" length of the array through other means (by telling it what the list's real Count
was).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With