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

Han*_*esh 5 c# arrays generics pinvoke unmanaged

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

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

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

我认为如果我确保T是一个结构,它可能会起作用

unsafe class FixedArray<T> where T : struct
Run Code Online (Sandbox Code Playgroud)

也不起作用......

我怎样才能在功能上等同于我上面要做的事情?

编辑:我正在使用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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Gab*_*abe 5

我很确定无法编译代码.对于你的代码(T*)arrayPtr + i,这将计算arrayPtr + i * 4何时T是a intarrayPtr + i * 8何时T是a 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 每次使用`this []`时,这个解决方案都会*将结构复制到内存块中,因此像`fixedArray [3] .Data = 3;`这样的合理语句将无法按预期工作.如果你想这样做,你也应该实现`Dispose`并使用`GC.AddMemoryPressure`. (2认同)

Ste*_*ary 5

您不需要非托管数组。

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

var myArray = new MyStruct[13];
Run Code Online (Sandbox Code Playgroud)

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

var handle = GCHandle.Alloc(myArray, GCHandleType.Pinned);
IntPtr address = handle.AddrOfPinnedObject();
Run Code Online (Sandbox Code Playgroud)

该数组将存在于该内存地址,直到您调用handle.Free. 如果您从不调用Free,那么它将一直保留在那里,直到您的程序退出。