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);
}
}
}
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?
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));
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With