列出到T 而不复制

我有一大堆需要提供给OpenGL的值类型。 如果这种情况尽快发生会很棒。 我现在正在做的是这样的:

List VList = new List(); ... //Add vertices Vertex[] VArray; VList.CopyTo(VArray, VList.Length); GL.SetData(..., VArray); 

这个列表很容易10MB大,所以复制很慢。 我可以不复制地执行此操作,就像以某种方式获取指向List内部使用的数组的指针一样?

或者我是否必须实现自己的List类..

编辑:我忘了提到我不知道将添加到列表中的元素数量。

IList 界面并不难做到(好吧,只要Reflector是免费且有效的,提示提示)。

您可以创建自己的实现并将内部数组公开为公共属性。

如果需要重复访问内部数组,最好将访问者存储为委托。

在此示例中,它是动态方法的委托。 第一次调用可能不会很快,但后续调用(在相同类型的列表上)将会快得多。

 public static class ListExtensions { static class ArrayAccessor { public static Func, T[]> Getter; static ArrayAccessor() { var dm = new DynamicMethod("get", MethodAttributes.Static | MethodAttributes.Public, CallingConventions.Standard, typeof(T[]), new Type[] { typeof(List) }, typeof(ArrayAccessor), true); var il = dm.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); // Load List argument il.Emit(OpCodes.Ldfld, typeof(List).GetField("_items", BindingFlags.NonPublic | BindingFlags.Instance)); // Replace argument by field il.Emit(OpCodes.Ret); // Return field Getter = (Func, T[]>)dm.CreateDelegate(typeof(Func, T[]>)); } } public static T[] GetInternalArray(this List list) { return ArrayAccessor.Getter(list); } } 

确保包括:

 using System.Reflection; using System.Reflection.Emit; 

我不建议你想做什么。 为什么你首先使用List ? 如果您可以准确地告诉我们您要创建的数据结构应该具有哪些特征,以及它应该如何与消费API进行交互,我们可能能够为您的问题提供适当的解决方案。

但我会按照要求回答这个问题。

我可以不复制地执行此操作,就像以某种方式获取指向List内部使用的数组的指针一样?

是的,尽管您将依赖于未记录的实现细节。 从.NET 4.0开始,支持数组字段称为_items

 Vertex[] vertices = (Vertex[]) typeof(List) .GetField("_items", BindingFlags.NonPublic | BindingFlags.Instance) .GetValue(VList); 

请注意,这个数组几乎肯定会在结尾处松弛 (这是List的整个点),所以这个数组上的array.Length不会那么有用。 消耗数组的API需要通过其他方式(通过告诉它列表的真实Count是什么)通知数组的“实际”长度。

而不是使用reflection来访问List的内部数组,如果您只需要添加的能力,那么我实际上建议您实现自己的可resize的数组(喘息!)。 这并不难。

就像是:

 class ResizableArray { T[] m_array; int m_count; public ResizableArray(int? initialCapacity = null) { m_array = new T[initialCapacity ?? 4]; // or whatever } internal T[] InternalArray { get { return m_array; } } public int Count { get { return m_count; } } public void Add(T element) { if (m_count == m_array.Length) { Array.Resize(ref m_array, m_array.Length * 2); } m_array[m_count++] = element; } } 

然后,您可以使用InternalArray获取内部数组,并使用Count知道数组中有多少项。

你可以用reflection做到这一点:

 public static T[] GetUnderlyingArray(this List list) { var field = list.GetType().GetField("_items", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); return (T[])field.GetValue(list); } 

编辑:啊,当我测试这个时,有人已经说过了..

您可能想要考虑您的方法是否错误。 如果你发现自己使用reflection来做到这一点 – 你已经迷失了。

我可以想到几种方法来解决这个问题,但哪一个是理想的,这在很大程度上取决于这是否是一个multithreading的代码片段。

我们假设它不是……

想想数组的特性。 每次调用此方法时,都会创建一个N长度数组。 您的目标是提高性能(这意味着您希望最小化分配和数据副本)。

你能否在编译或运行时暗示数组的理想起始大小是什么? 我的意思是 – 如果95%的时间N长度是100k或更少……从100k项目arrays开始。 继续使用它,直到遇到arrays太小的情况。

当你遇到这种情况时,你可以根据你对程序的理解来决定你做了什么。 arrays应该增长10%吗? 它应该增长到文字所需的长度吗? 您可以使用您拥有的内容并继续处理其余数据吗?

随着时间的推移,将找到理想的尺寸。 您甚至可以让程序在每次运行时监视最终大小,并在下次启动时将其用作分配提示(可能此数组长度取决于环境因素,如分辨率等)。

换句话说 – 我建议你不要使用List-to-Array方法,并预先分配一个数组,永久保存它,并根据需要增长它。

如果您的程序存在线程问题,您显然需要解决这些问题。

您可能能够从通用List中获取指针,但我不推荐它,它可能不会按照您期望的方式工作(如果有的话)。 基本上它意味着获取指向对象的指针,而不是像数组那样的内存结构。

我认为你应该以相反的方式解决这个问题,如果你需要速度,那么在不安全的上下文中使用结构数组指针直接在字节数组上工作。

背景资料:
“即使与unsafe关键字一起使用,也不允许获取托管对象的地址,获取托管对象的大小或声明指向托管类型的指针。” – 从C#:将通用指针转换为数组

MSDN不安全

既然你正在使用GL,我会假设你知道你在做什么,并跳过所有警告。 试试这个,或者访问https://stackoverflow.com/a/35588774/194921

  [StructLayout(LayoutKind.Explicit)] public struct ConvertHelper 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 { 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 { public T[] _items; } public static T[] GetBackingArray(this List list) { return ConvertHelper, PublicList>.Convert(list)._items; }