How can I create a Memory<T> from a Span<T>?
I'm trying to overload a parsing method to use a ReadOnlySpan<char>
parameter in addition to the string
version. The problem is that the implementation uses a Dictionary<string, T>
for the parsing logic.
I tried switching it to a Dictionary<ReadOnlySpan<char>, T>
but of course that didn't work as ReadOnlySpan<char>
isn't allowed as a generic parameter since it's a stack only object. I then switched it to using ReadOnlyMemory<char>
which is allowed. I then implemented a basic Ordinal
comparer but am now having troubles creating a ReadOnlyMemory<char>
from the ReadOnlySpan<char>
parameter. Is this possible?
Update
It seems that this is not possible. In order to support the scenario I've posted above I will change the dictionary to have an int key which is the hashcode of the ReadOnlySpan<char>
and make the value a list with the string embedded in the element and manually have to resolve hash code collisions.
TL;DR:
You can go Memory<T>
to Span<T>
, but not the other way around.
Background:
There is an informative 2018 MSDN Magazine article article that introduces Span<T>
in a 2018 MSDN Magazine article
Span<T>
instances can only live on the stack, not on the heap. This means you can’t box spans (and thus can’t useSpan<T>
with existing reflection invoke APIs, for example, as they require boxing). It means you can’t haveSpan<T>
fields in classes, or even in non-ref-like structs. It means you can’t use spans in places where they might implicitly become fields on classes, for instance by capturing them into lambdas or as locals in async methods or iterators (as those “locals” may end up being fields on the compiler-generated state machines.) It also means you can’t useSpan<T>
as a generic argument, as instances of that type argument could end up getting boxed or otherwise stored to the heap (and there’s currently no “where T : ref struct
” constraint available)....
You can create a
Memory<T>
from an array and slice it just as you would a span, but it’s a (non-ref-like) struct and can live on the heap. Then, when you want to do synchronous processing, you can get aSpan<T>
from it, for example:static async Task<int> ChecksumReadAsync(Memory<byte> buffer, Stream stream) { int bytesRead = await stream.ReadAsync(buffer); return Checksum(buffer.Span.Slice(0, bytesRead)); // Or buffer.Slice(0, bytesRead).Span } static int Checksum(Span<byte> buffer) { ... }
I think his article clears it up better than me writing my own answer.