How to get a ReadOnlySpan<byte> from a readonly struct?

It looks like this works:

// The following code will work from C# 7.3 and up, no unsafe keyword required
Span<MyStruct> span = stackalloc MyStruct[1];
span[0] = new MyStruct(3, 4);
var bytes = MemoryMarshal.Cast<MyStruct, byte>(span);

If we wanted to expose it as a property, we could try the following:

// Will not work at runtime
public ReadOnlySpan<byte> Span
{
    get
    {
        unsafe
        {
            fixed (MyStruct* ptr = &this)
            {
                return new ReadOnlySpan<byte>(ptr, sizeof(MyStruct)); // If on the heap, we're doomed as returning will unpin the memory.
            }
        }
    }
}

And marking the struct as a readonly ref struct, this guards us again the struct ever being on the heap. This compiles, but doesn't run as you get a AccessViolationException at runtime. I will do some more digging to see if it's possible, it should be logically safe to do, but may not be possible today.

Another compromise solution is to keep it as a readonly struct (not ref struct) and add this static method:

public static unsafe ReadOnlySpan<byte> GetSpan(ref MyStruct myStruct)
{
    return new ReadOnlySpan<byte>(Unsafe.AsPointer(ref myStruct), sizeof(MyStruct));
}

Then from calling code:

var myStruct = new MyStruct(1, 2);
var span = MyStruct.GetSpan(ref myStruct);

We can improve the usage of this by moving it out into a ref extensions methods (A C# 7.2 feature):

class Program
{
    static void Main()
    {
        var myStruct = new MyStruct(1, 2);
        var span = myStruct.GetSpan();
    }
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public readonly struct MyStruct
{
    public readonly int Field1;
    public readonly int Field2;

    public MyStruct(int field1, int field2) => (Field1, Field2) = (field1, field2);
}

public static class MyStructExtensions
{
    public static unsafe ReadOnlySpan<byte> GetSpan(ref this MyStruct myStruct)
    {
        return new ReadOnlySpan<byte>(Unsafe.AsPointer(ref myStruct), sizeof(MyStruct));
    }
}

Tags:

C#

.Net Core