.NET 框架中的 Span<T> 和流

Span<T> and streams in .NET framework

我正在使用网络缓冲区和流,Span 和 Memory 非常适合应用程序要求。

根据 问题,我想让一个 Stream 接受 Span 作为参数。我知道这是在 .NET Core 2.1 中实现的,但我想知道是否也有办法在 .NET Framework 中获得此功能? (我使用的是 4.7.1)

类似于:

Span<Byte> buffer = new Span<byte>();
stream.Read(buffer);

遗憾的是,由于此功能尚未在 .Net Standard 中实现,因此未包含在 .Net Framework 中。

编辑:我记得我从某处读到有预发布的 NuGet 包可以与 .Net Framework 一起使用

使用 System.Memory

检查 NuGet

我设法通过为 Stream class 编写扩展方法并实现 .NET Core 处理 Span 的默认行为来解决这个问题。

    public static int Read(this Stream thisStream, Span<byte> buffer)
    {
        byte[] sharedBuffer = ArrayPool<byte>.Shared.Rent(buffer.Length);
        try
        {
            int numRead = thisStream.Read(sharedBuffer, 0, buffer.Length);
            if ((uint)numRead > (uint)buffer.Length)
            {
                throw new IOException(SR.IO_StreamTooLong);
            }
            new Span<byte>(sharedBuffer, 0, numRead).CopyTo(buffer);
            return numRead;
        }
        finally { ArrayPool<byte>.Shared.Return(sharedBuffer); }
    }

    public static void Write(this Stream thisStream, ReadOnlySpan<byte> buffer)
    {
        byte[] sharedBuffer = ArrayPool<byte>.Shared.Rent(buffer.Length);
        try
        {
            buffer.CopyTo(sharedBuffer);
            thisStream.Write(sharedBuffer, 0, buffer.Length);
        }
        finally { ArrayPool<byte>.Shared.Return(sharedBuffer); }
    }

公认的解决方案非常有效!

但是,在某些情况下,当我同时使用同时针对 .NET Core 和 .NET Framework 的共享项目时,我会同时使用数组和 Span<T>:

var _buffer = new byte[1024*1024];
Span<Byte> buffer = _buffer;

#if NETCOREAPP3_1

stream.Read(buffer);

#else

stream.Read(_buffer);

#endif

通过这种方式,我可以避免 renting/copying/returning 来自 ArrayPool 的数组的小开销。