列出<T>到T []而不复制

Han*_*esh 27 c# arrays list

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

List<Vertex> VList = new List<Vertex>();
... //Add vertices
Vertex[] VArray;
VList.CopyTo(VArray, VList.Length);
GL.SetData(..., VArray);
Run Code Online (Sandbox Code Playgroud)

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

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

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

Ond*_*lka 26

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

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

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

确保包括:

using System.Reflection;
using System.Reflection.Emit;
Run Code Online (Sandbox Code Playgroud)


Ani*_*Ani 13

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

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

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

是的,尽管您将依赖于未记录的实现细节.从.NET 4.0开始,调用后备阵列字段_items.

Vertex[] vertices = (Vertex[]) typeof(List<Vertex>)
                   .GetField("_items", BindingFlags.NonPublic | BindingFlags.Instance)
                   .GetValue(VList);
Run Code Online (Sandbox Code Playgroud)

请注意,这个数组几乎肯定会在最后(这是整个点)松弛List<T>,所以array.Length在这个数组上将不会那么有用.消耗数组的API需要通过其他方式(通过告诉它列表的真实内容Count)通知数组的"实际"长度.

  • 只是交叉指示MS不会在下一个版本中更改内部实现... (4认同)

小智 8

所述的IList <T>接口并不难做到(当然,不是只要反射是免费的和功能,暗示提示).

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

  • 我什至不会理会`IList&lt;T&gt;`,只需包装一个`T[]` 并引入一个`Add` 方法。`IndexOf`、`Insert` 等——不需要。 (2认同)
  • @Will:是的,假设。对我来说听起来像是 YAGNI 的情况。尤其是非通用的“IList”部分(完全是另一回事!)。但既然我不知道他可能会用它来做什么*其他*,你是对的;我可能完全错了。让我这样说:如果实际上我所需要的只是一个我可以添加并仍然作为数组访问的数组,那么我绝对不会费心去实现`IList&lt;T&gt;`。也许`IEnumerable&lt;T&gt;`,只是因为它太琐碎了。 (2认同)

dan*_*il_ 7

这不是一个好方法,但您可以使用CollectionsMarshal.AsSpan(自 .net 5 起)。它可以访问内部列表数组(参见源代码)。

var list = new List<int>();
CollectionsMarshal.AsSpan(list);
Run Code Online (Sandbox Code Playgroud)

https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.collectionsmarshal.asspan?view=net-6.0


Dan*_*Tao 5

List<T>如果你只需要添加的能力,而不是使用反射来访问内部数组,那么我实际上建议你实现自己的可调整大小的数组(喘息!).这并不难.

就像是:

class ResizableArray<T>
{
    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;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以使用内部数组InternalArray并知道数组中有多少项Count.

  • @Iain:我也不会,但如果它可以接受一个 `length` 参数,那就太完美了。否则我不知道 OP 真正希望的是什么;我怀疑他是否期望 `List&lt;T&gt;` 在每次调用 `Add` 时将其内部数组的大小调整 **1**(想象一下,保证 O(N) 追加!)。 (2认同)