首页 > 解决方案 > 如何在包含本机内存的类上实现 AsSpan()?

问题描述

我有一个包含一些本机内存的类型。我想AsSpan()在它上面实现,以便可以安全地访问本机内存。

下面是一个天真的尝试,它显示了我正在尝试做的事情。显然Span<T>不会让持有者对象保持活动状态,因为它只有指向本机内存的指针,而不是对持有者对象的引用。我也尝试过实现MemoryManager<T>,但我也看不到如何使Span<T>管理器对象保持活动状态。

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

internal class Program
{
    public static void Main()
    {
        Span<byte> span = GetSpan();
        GC.Collect();
        GC.WaitForPendingFinalizers();
        span[span.Length - 1] = 0;  // Access violation
    }

    private static Span<byte> GetSpan()
    {
        Span<byte> span = new Holder(1 << 20).AsSpan();
        GC.Collect();
        GC.WaitForPendingFinalizers();
        span[span.Length - 1] = 0;  // OK
        return span;
    }
}

public class Holder
{
    private readonly int _size;
    private readonly IntPtr _pointer;

    public Holder(int size)
    {
        _size = size;
        _pointer = Marshal.AllocHGlobal(_size);
    }

    ~Holder()
    {
        Marshal.FreeHGlobal(_pointer);
        Debug.WriteLine("{0:X} was freed", _pointer);
    }

    public Span<byte> AsSpan()
    {
        unsafe { return new Span<byte>((void*)_pointer, _size); }
    }
}

Span<T>能够保持被管理的对象像Stringbyte[]活着。有没有办法AsSpan()在本机内存持有者类型上实现,以便 Span<T>让持有者对象保持活动状态?

标签: c#.net

解决方案


根据这个讨论,非托管内存的生命周期无法使用Span<T>.

您可以实现MemoryManager<T>管理非托管内存的生命周期,在一个using块中使用它,只要Span<T>不逃逸 using 块,它就是安全的。(但不可能强制 API 使用者安全使用 API。)


推荐阅读