C#使用Memory <T>或ArraySegment <T>访问非托管数组?

Red*_*ood 7 c# unmanaged-memory

通过引入的Memory,Span并且ArraySegment在C#7.2,我想知道如果我可以代表一个非托管数组作为枚举对象,即生活在堆上.

后一个要求排除了Span,它基本上实现了我想要的东西:例如

unsafe { bytes = new Span<byte>((byte*)ptr + (index * Width), Width); 
Run Code Online (Sandbox Code Playgroud)

是否可以用ArraySegment或做同样的事情Memory?他们的构造函数只接受byte[],也许有某种方法可以欺骗C#byte*而不是传递byte[]

Mar*_*ell 12

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

现在你可以使用:

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

并且memory可以存储在堆上.

然而,最大限度地减少分配你可能想创建一个单一的 UnmanagedMemoryManager<byte>覆盖整个区域-只有一次-然后使用.Slice(...).Memory代表整个区域.这样你就有了一个对象和很多切片(切片是结构,而不是对象).

请注意,此实现假设您将控制其他位置的内存生命周期 - Dispose()此处不会尝试通过Marshal等方式释放内存.

  • @AaronLS 也许它需要一个经理_因为_它是“不受管理的”?;-) (3认同)
  • `UnmanagedMemoryManager`-如果我曾经看过一个悖论的合法用途。 (2认同)