在C#中实现通用的非托管数组

时间:2011-03-22 07:59:33

标签: c# arrays generics pinvoke unmanaged

我正在用C#实现一个非托管数组类,我需要一些OpenGL调用。

这很好,但我遇到了障碍。以下代码无法编译,我理解为什么,但我怎样才能使它工作?

    public T this[int i]
    {
        get { return *((T*)arrayPtr + i); }
        set { *((T*)arrayPtr + i) = value; }
    }

我认为如果我确保T是结构

,它可能会有用
unsafe class FixedArray<T> where T : struct

也不起作用......

我怎样才能在功能上与我上面尝试的内容相提并论?

编辑:我正在使用Marshal.AllocHGlobal()的非托管数组,以便我的数组被修复,GC不会移动它。当你调用它时,OpenGL实际上并不处理指令,OpenGL会在函数返回后很长时间内尝试访问数组。

如果这有帮助,这就是全班:

unsafe class FixedArray<T> where T : struct
{
    IntPtr arrayPtr;

    public T this[int i]
    {
        get { return *((T*)arrayPtr + i); }
        set { *((T*)arrayPtr + i) = value; }
    }
    public FixedArray(int length)
    {
        arrayPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(T)) * length);
    }
    ~FixedArray()
    {
        Marshal.FreeHGlobal(arrayPtr);
    }
}

错误消息是无法获取地址,获取大小,或声明指向托管类型的指针('T')

3 个答案:

答案 0 :(得分:5)

我很确定无法编译代码。对于您的代码(T*)arrayPtr + i,当arrayPtr + i * 4T时会计算intarrayPtr + i * 8T时会计算long。由于CLR无法替换4,8或其他任何sizeof(T),因此该代码无效。

您需要做的是使用Marshal为您完成所有工作。编译,但我没有测试过:

unsafe class FixedArray<T> where T : struct
{
    IntPtr arrayPtr;

    int sizeofT { get { return Marshal.SizeOf(typeof(T)); } }

    public T this[int i]
    {
        get
        {
            return (T)Marshal.PtrToStructure(arrayPtr + i * sizeofT, typeof(T));
        }
        set
        {
            Marshal.StructureToPtr(value, arrayPtr + i * sizeofT, false);
        }
    }
    public FixedArray(int length)
    {
        arrayPtr = Marshal.AllocHGlobal(sizeofT * length);
    }
    ~FixedArray()
    {
        Marshal.FreeHGlobal(arrayPtr);
    }
}

答案 1 :(得分:4)

您不需要非托管数组。

您可以正常声明托管数组:

var myArray = new MyStruct[13];

然后使用GCHandle类将其固定在内存中并获取其地址:

var handle = GCHandle.Alloc(myArray, GCHandleType.Pinned);
IntPtr address = handle.AddrOfPinnedObject();

在您调用handle.Free之前,该数组将存在于该内存地址中。如果您从未致电Free,那么它将一直待在您的程序退出之前。

答案 2 :(得分:0)

现在在c#7.3中是可能的。您需要指定unmanaged通用约束:

unsafe class FixedArray<T> where T : unmanaged
{
    T* arrayPtr;

    public T this[int i]
    {
        get { return *(arrayPtr + i); }
        set { *(arrayPtr + i) = value; }
    }
    public FixedArray(int length)
    {
        arrayPtr = (T*)Marshal.AllocHGlobal(sizeof(T) * length);
    }
    ~FixedArray()
    {
        Marshal.FreeHGlobal((IntPtr)arrayPtr);
    }
}

不幸的是仍然存在局限性。例如,类型T不能包含通用类型,因此FixedArray暂时不能包含其他FixedArray