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

Ste*_*tze 5 c# .net-core

The following code causes the compiler to throw error CS1605 ("Cannot pass 'var' as a ref or out argument because it is read-only") in the first line of the property getter.

[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 ReadOnlySpan<byte> Span
    {
        get
        {
            // This code only works when MyStruct is not read only
            ReadOnlySpan<MyStruct> temp = MemoryMarshal.CreateReadOnlySpan(ref this, 1);
            return MemoryMarshal.Cast<MyStruct, byte>(temp);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Removing the readonly from the public readonly struct MyStruct line makes the code work, but for performance reasons, I would really like the struct to be readonly. It makes the code so much more cleaner than having to pass the struct as ref all the time.

Is there a way to get a ReadOnlySpan<byte> from a readonly struct?

Edit: ... without creating an implicit or explicit copy of the structure?

Stu*_*art 6

看起来像这样:

// 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);
Run Code Online (Sandbox Code Playgroud)

如果我们想将其作为属性公开,我们可以尝试以下操作:

// 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.
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

并将结构标记为 a readonly ref struct,这再次保护了我们在堆上的结构。这会编译,但不会像您AccessViolationException在运行时获得的那样运行。我会做更多的挖掘,看看是否有可能,这样做在逻辑上应该是安全的,但今天可能不可能。

另一个折衷解决方案是将其保留为readonly struct(not ref struct) 并添加此静态方法:

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

然后从调用代码:

var myStruct = new MyStruct(1, 2);
var span = MyStruct.GetSpan(ref myStruct);
Run Code Online (Sandbox Code Playgroud)

我们可以通过将它移到 ref 扩展方法(AC# 7.2 功能)中来改进它的使用:

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));
    }
}
Run Code Online (Sandbox Code Playgroud)