Why is it not possible to use both readonly and fixed-size buffers in structs in C# 7.2
Asked Answered
N

2

7

With the release of C# 7.2 there is now the ability to have readonly structs, which in many cases can improve performance.

For one of my structs I am using a fixed-size byte array to actually hold the data. However when I marked the struct and the byte array field readonly, C# compiler complained that readonly is not valid on the field. Why can't I have both fixed and readonly on a field in a struct?

readonly unsafe struct MyStruct {
  readonly fixed byte _Value[6]; //The modifier 'readonly' is not valid for this item.
}
Nacred answered 10/12, 2017 at 6:28 Comment(2)
That has always been this way, doesn't have anything to do with 7.2. Just consider what you are going to do with that field, hopefully you use byte* to access it. No way for the compiler to enforce readonly on that pointer. If you don't use byte* then there is surely a better approach, we can't tell from the question.Pederson
With C# 7.3, fixed buffers can be accessed without pinning. So I guess a compiler can disallow using fixed altogether for a theoretical "readonly fixed buffer" (so we can't get it as a pointer) and allow set access only in the constructor.Virgenvirgie
S
1

Because C# specification says so (and it always did, even before c# 7.2). In 18.7.1 section, named "Fixed size buffer declarations", the following modifiers are allowed on fixed buffer declaration:

new

public

protected

internal

private

unsafe

No readonly here. If you think about it - it doesn't make much sense anyway, because fixed buffer size is represented by a pointer, and you cannot restict write access to a pointer. For example:

var s = new MyStruct();
byte* value = s._Value;
// how can you prevent writing to `byte*`?
Sidedress answered 10/12, 2017 at 9:24 Comment(1)
it doesn't make much sense anyway Only in the context of C# not supporting pointer-to-const (or in C# language, pointer-to-readonly). Many languages, including C and C++, do support some syntax denoting that the "pointed-to thing" is immutable, at least at compile-time. If C# allowed readonly fixed then it would logically decay to (the equivalent of) a const T* const. The pointer itself would be const because the buffer already can't be reassigned to point to something else. The missing piece that C# doesn't support (yet) is just that first const.Carmelo
R
0

Actually there is a trick to have a readonly fixed field in a readonly struct: just embed your fixed inside a dedicated struct ;-)

unsafe struct MyFixed {
    public fixed byte _v[6];

    public MyFixed(byte[] bs)
    {
        for (int i=0;i!=6;i++)
            _v[i]=bs[i];
    }
 }

readonly struct MyStruct {
    readonly public MyFixed Values;

    public MyStruct(byte[] bs){
        Values = new MyFixed(bs);
    }

static unsafe byte GetFixed(in MyStruct _ms)
=> _ms.Values._v[1];
        
static unsafe byte SetFixed(in MyStruct _ms)
=> _ms.Values._v[1] = 0;

I added:

  • a constructor just to be able to test sharplab.
  • Get and Set from outside, the compiler detect and report a read-only access in the Set.

This clearly demonstrate that the limitation you faced is just arbitrary and could easily be fixed (pun intended) in Roslyn ;-)

Revile answered 28/3, 2023 at 0:41 Comment(1)
I actually used this trick recently, since having read-only structs avoid most of the hidden copies when passing said structs by 'in'. So a useful trick, at the cost of slightly more verbose user-side code...Revile

© 2022 - 2024 — McMap. All rights reserved.