C # доступ к неуправляемому массиву с использованием Memory <T>или ArraySegment <T>? - PullRequest
0 голосов
/ 05 сентября 2018

С введением Memory, Span и ArraySegment в C # 7.2 мне стало интересно, смогу ли я представить неуправляемый массив как перечисляемый объект, который живет в куче.

Это последнее требование исключает Span, которое в основном реализовало именно то, что я хотел: например,

unsafe { bytes = new Span<byte>((byte*)ptr + (index * Width), Width); 

Можно ли сделать то же самое с ArraySegment или Memory? Их конструкторы принимают только byte[], может быть, есть какой-то способ обмануть C # в передаче byte* вместо byte[]?

1 Ответ

0 голосов
/ 05 сентября 2018

Да для Memory<T>, но вам нужно создать свой собственный MemoryManager<T>. Не волнуйтесь - это не так страшно, как кажется - вот тот, который я написал ранее ... :

/// <summary>
/// A MemoryManager over a raw pointer
/// </summary>
/// <remarks>The pointer is assumed to be fully unmanaged, or externally pinned - no attempt will be made to pin this data</remarks>
public sealed unsafe class UnmanagedMemoryManager<T> : MemoryManager<T>
    where T : unmanaged
{
    private readonly T* _pointer;
    private readonly int _length;

    /// <summary>
    /// Create a new UnmanagedMemoryManager instance at the given pointer and size
    /// </summary>
    /// <remarks>It is assumed that the span provided is already unmanaged or externally pinned</remarks>
    public UnmanagedMemoryManager(Span<T> span)
    {
        fixed (T* ptr = &MemoryMarshal.GetReference(span))
        {
            _pointer = ptr;
            _length = span.Length;
        }
    }
    /// <summary>
    /// Create a new UnmanagedMemoryManager instance at the given pointer and size
    /// </summary>
    public UnmanagedMemoryManager(T* pointer, int length)
    {
        if (length < 0) throw new ArgumentOutOfRangeException(nameof(length));
        _pointer = pointer;
        _length = length;
    }
    /// <summary>
    /// Obtains a span that represents the region
    /// </summary>
    public override Span<T> GetSpan() => new Span<T>(_pointer, _length);

    /// <summary>
    /// Provides access to a pointer that represents the data (note: no actual pin occurs)
    /// </summary>
    public override MemoryHandle Pin(int elementIndex = 0)
    {
        if (elementIndex < 0 || elementIndex >= _length)
            throw new ArgumentOutOfRangeException(nameof(elementIndex));
        return new MemoryHandle(_pointer + elementIndex);
    }
    /// <summary>
    /// Has no effect
    /// </summary>
    public override void Unpin() { }

    /// <summary>
    /// Releases all resources associated with this object
    /// </summary>
    protected override void Dispose(bool disposing) { }
}

Теперь вы можете использовать:

var mgr = new UnmanagedMemoryManager((byte*)ptr + (index * Width), Width);
Memory<byte> memory = mgr.Memory;

и memory могут храниться в куче.

Однако, чтобы минимизировать выделения, вы, вероятно, захотите создать один UnmanagedMemoryManager<byte>, который охватывает весь регион - только один раз - и затем использовать .Slice(...) на .Memory это представляет весь регион. Таким образом, у вас есть один объект и множество фрагментов (фрагменты - это структуры, а не объекты).

Обратите внимание, что эта реализация предполагает, что вы собираетесь управлять временем жизни памяти в другом месте - Dispose() здесь не не пытается освободить память через Marshal и т. Д.

...