C# 使用 Memory<T> 或 ArraySegment<T> 访问非托管数组?
C# access unmanaged array using Memory<T> or ArraySegment<T>?
随着 C# 7.2 中 Memory
、Span
和 ArraySegment
的引入,我想知道是否可以将非托管数组表示为可枚举对象,它位于堆上.
后一个要求排除了 Span
,它基本上实现了我想要的:例如
unsafe { bytes = new Span<byte>((byte*)ptr + (index * Width), Width);
是否可以对 ArraySegment
或 Memory
做同样的事情?他们的构造函数只接受 byte[]
,也许有一些方法可以欺骗 C# 传递 byte*
而不是 byte[]
?
是 Memory<T>
,但您需要创建自己的 MemoryManager<T>
。别担心 - 这并不像听起来那么可怕 - here's one I wrote earlier...:
/// <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>
- 仅一次- 然后在代表整个区域的 .Memory
上使用 .Slice(...)
。这样你就有了一个对象和很多切片(切片是结构,而不是对象)。
请注意,此实现假定您将控制其他地方的内存生命周期 - 这里的 Dispose()
不会 尝试通过 [= 释放内存19=]等
随着 C# 7.2 中 Memory
、Span
和 ArraySegment
的引入,我想知道是否可以将非托管数组表示为可枚举对象,它位于堆上.
后一个要求排除了 Span
,它基本上实现了我想要的:例如
unsafe { bytes = new Span<byte>((byte*)ptr + (index * Width), Width);
是否可以对 ArraySegment
或 Memory
做同样的事情?他们的构造函数只接受 byte[]
,也许有一些方法可以欺骗 C# 传递 byte*
而不是 byte[]
?
是 Memory<T>
,但您需要创建自己的 MemoryManager<T>
。别担心 - 这并不像听起来那么可怕 - here's one I wrote earlier...:
/// <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>
- 仅一次- 然后在代表整个区域的 .Memory
上使用 .Slice(...)
。这样你就有了一个对象和很多切片(切片是结构,而不是对象)。
请注意,此实现假定您将控制其他地方的内存生命周期 - 这里的 Dispose()
不会 尝试通过 [= 释放内存19=]等