读取流:Byte[] vs Memory<Byte> vs Span<Byte>

10

这两者之间的基本区别是什么:

var buffer = new byte[8192];
var bytesRead = Stream.Read(buffer, 0, buffer.Length);

var buffer = new byte[8192];
var span = new Span<byte>(buffer);
var bytesRead = Stream.Read(span);

var buffer = new byte[8192];
var memory = new Memory<byte>(buffer);
var bytesRead = Stream.ReadAsync(memory).Result;

除了它们都是不同的对象类型,以及唯一一个接受Memory<Byte>Stream.Read()方法当然是ReadAsync(),这是显而易见的事实。

为什么我会选择byte[]Span<byte>Memory<byte>中的任何一种?我如何决定哪种最适合我的情况?


请参阅 https://learn.microsoft.com/en-us/dotnet/standard/memory-and-spans/memory-t-usage-guidelines。 - Julian
@Julian 谢谢你提供的链接,不过我已经看过那篇文档,还是不太清楚这两者之间的区别以及何时该使用哪一个。特别是因为文档只关注抽象实现(Span<T>Memory<T>),没有真实的用例或在此情况下与 byte[] 的比较。 - cogumel0
1
你需要选择你需要的那个。例如,如果你有一个字节数组,你会使用字节数组版本,而如果你有一个Memory/Span,你会使用那个。当然,现在也有从数组到Span的隐式转换,大多数字节数组方法只是转发到Span方法。如果你正在设计API,你应该使用像Memory或Span这样的抽象,以便给你的调用者一个选择。Span可以是零分配,也可以是未管理内存的包装器或其他东西。而字节数组始终只是一个字节数组(通常是一个分配,除非使用Array pool)。 - pinkfloydx33
是你的其他代码使用接收到的字节来确定哪种方法更好,所以我认为目前的问题无法引导出有意义的讨论。 - Lex Li
@pinkfloydx33 我希望有一个单一类型可以抽象IntPtrbyte*Memory<Byte>Span<Byte>Byte[]以及之间的所有其他类型(如ArraySegment<Byte>IList<Byte>IReadOnlyList<Byte>等)等等... 叹气 - Dai
1个回答

4
假设这个问题是关于内存/跨度/数组而不是Read/ReadAsync的,那么在这个示例中没有根本性的区别。
区别在于Memory/Span是内存的抽象,有点像安全指针。它们可以表示常规的C#数组,也可能表示未经处理的内存。它们还可以表示另一种类型的内存。因此,如果某些代码将数据加载到字节数组中,则可以将其转换为int数组而无需进行大量复制。如果您不想让方法访问所有数据,则它还支持切片。
Memory和Span之间的区别主要在于Span更有效率,但在使用方式上有一些限制。
因此,请使用适合您拥有的数据的类型。如果您正在设计API,则通常最好选择最通用类型,即Span/ReadOnlySpan,并可能添加方便的重载或扩展方法。

1
实际上文档中说Memory可以指向未托管的内存,但在Net 6.0中却没有允许这样做的构造函数。这真是一种抽象。 - cineam mispelt

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接