Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

List<T> to T[] without copying

Tags:

arrays

c#

list

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.

like image 264
Hannesh Avatar asked Feb 11 '11 19:02

Hannesh


People also ask

In what situation should you use a List T instead of an array of T?

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.

Are lists always passed by reference in C#?

List<T> is a class, and all class instances are passed by reference.

Does ToList create a copy C#?

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.


2 Answers

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; 
like image 159
Ondrej Petrzilka Avatar answered Sep 19 '22 10:09

Ondrej Petrzilka


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).

like image 21
Ani Avatar answered Sep 23 '22 10:09

Ani