C# 7.2 中的 Span<T> 和 Memory<T> 有什么区别?

What is the difference between Span<T> and Memory<T> in C# 7.2?

C# 7.2 引入了两种新类型:Span<T>Memory<T>,它们比 string[].

等早期 C# 类型具有更好的性能

问题:Span<T>Memory<T>有什么区别?为什么我要用一个而不是另一个?

Span<T> 本质上是仅堆栈的,而 Memory<T> 可以存在于堆上。

Span<T> is a new type we are adding to the platform to represent contiguous regions of arbitrary memory, with performance characteristics on par with T[]. Its APIs are similar to the array, but unlike arrays, it can point to either managed or native memory, or to memory allocated on the stack.

Memory <T> is a type complementing Span<T>. As discussed in its design document, Span<T> is a stack-only type. The stack-only nature of Span<T> makes it unsuitable for many scenarios that require storing references to buffers (represented with Span<T>) on the heap, e.g. for routines doing asynchronous calls.

async Task DoSomethingAsync(Span<byte> buffer) {
    buffer[0] = 0;
    await Something(); // Oops! The stack unwinds here, but the buffer below
                       // cannot survive the continuation.
    buffer[0] = 1;
}

To address this problem, we will provide a set of complementary types, intended to be used as general purpose exchange types representing, just like Span <T>, a range of arbitrary memory, but unlike Span <T> these types will not be stack-only, at the cost of significant performance penalties for reading and writing to the memory.

async Task DoSomethingAsync(Memory<byte> buffer) {
    buffer.Span[0] = 0;
    await Something(); // The stack unwinds here, but it's OK as Memory<T> is
                       // just like any other type.
    buffer.Span[0] = 1;
}

In the sample above, the Memory <byte> is used to represent the buffer. It is a regular type and can be used in methods doing asynchronous calls. Its Span property returns Span<byte>, but the returned value does not get stored on the heap during asynchronous calls, but rather new values are produced from the Memory<T> value. In a sense, Memory<T> is a factory of Span<T>.

参考文档:here

re: 这意味着它只能指向在堆栈上分配的内存。

Span<T> 可以指向任何内存:分配在堆栈或堆上。 Span<T> 的仅堆栈性质意味着 Span<T> 本身(不是它指向的内存)必须仅驻留在堆栈上。这与 "normal" C# 结构相反,后者可以驻留在堆栈或堆上(通过值类型装箱,或者当它们嵌入 classes/reference-types 中时)。一些更明显的实际意义是你不能在 class 中有一个 Span<T> 字段,你不能装箱 Span<T>,你不能制作它们的数组。

Memory<T> 可以看作是 Span<T> 的不安全但更通用的版本。如果 Memory<T> 对象指向已释放的数组,则访问该对象将失败。