Реализация универсального неуправляемого массива в C # - PullRequest
5 голосов
/ 22 марта 2011

Я реализую класс неуправляемого массива в 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 ]

5 голосов
/ 22 марта 2011

Я почти уверен, что получить ваш код для компиляции невозможно. Для вашего кода (T*)arrayPtr + i это будет вычислять arrayPtr + i * 4, когда T является int и arrayPtr + i * 8, когда T является 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);
    }
}
4 голосов
/ 22 марта 2011

Вам не нужен неуправляемый массив.

Вы можете объявить управляемый массив обычно:

var myArray = new MyStruct[13];

, а затем использовать класс GCHandle, чтобы закрепить его в памяти и получитьего адрес:

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

Массив будет существовать по этому адресу в памяти, пока вы не вызовете handle.Free.Если вы никогда не позвоните Free, он останется там до тех пор, пока ваша программа не закроется.

0 голосов
/ 05 июля 2018

Теперь это возможно в 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 s.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...