MemoryMarshal.CreateReadOnlySpan has this signature:
public static ReadOnlySpan<T> CreateReadOnlySpan<T> (ref T reference, int length);
Notice that it requires a ref parameter. In other words you can't use it to do this:
[StructLayout(LayoutKind.Sequential, Size = 512)]
struct LargeStruct
{}
class Program
{
static readonly LargeStruct Blob = default;
static byte GetSomethingFrom(in LargeStruct blob)
{
ReadOnlySpan<byte> span = MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(in blob, 1));
// ^^^^^^^ Argument is 'in' while parameter is declared as 'ref'
return span[5];
}
static void Main()
{
Console.WriteLine(GetSomethingFrom(in Blob));
}
}
Of course I cannot take a ref of Blob because it's a readonly field.
So I'm left with doing something like this:
[StructLayout(LayoutKind.Sequential, Size = 512)]
struct LargeStruct
{}
class Program
{
static readonly LargeStruct Blob = default;
static unsafe byte GetSomethingFrom(in LargeStruct blob)
{
fixed(LargeStruct* pointer = &blob)
{
ReadOnlySpan<byte> span = MemoryMarshal.AsBytes(new ReadOnlySpan<LargeStruct>(pointer, 1));
return span[5];
}
}
static void Main()
{
Console.WriteLine(GetSomethingFrom(in Blob));
}
}
I'm wondering if it is possible to encapsulate this behavior into a method having the following signature:
public static ReadOnlySpan<T> CreateReadOnlySpan<T> (in T reference, int length);
My attempt
Here is my attempt so far:
using System;
using System.Runtime.CompilerServices;
public static class MemoryMarshal2
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe ReadOnlySpan<T> CreateReadOnlySpan<T>(in T value, int length)
where T : unmanaged
{
fixed(T* pointer = &value)
{
return new ReadOnlySpan<T>(pointer, length);
}
}
}
However, I'm not sure this usage of the fixed keyword is appropriate. The documentation says
Pointers to movable managed variables are useful only in a
fixedcontext.
...and yet here I am "leaking" that pointer out of the fixed context (through the returned ReadOnlySpan). And I think the Blob variable in my examples above could be moved by GC?