Are readonly structs supposed to be immutable when in an array?
Asked Answered
C

1

12

(Note: This sample code requires C# 7.2 or later, and the Nuget System.Memory package.)

Let's suppose we have a readonly struct as follows:

public readonly struct Test
{
    public Test(int value)
    {
        Value = value;
    }

    public int Value { get; }
}

Now let's put it into an array:

var array = new Test[] { new Test(1) };

Console.WriteLine(array[0].Value); // Prints 1

So far so good. You cannot write code to modify array[0].Value directly.

Now suppose we do this:

array.AsSpan().AsBytes()[3] = 1;

Console.WriteLine(array[0].Value); // Prints 16777217

So now we've modified the Value component of the readonly struct in the array.

Is this behaviour correct?

Crossbench answered 12/12, 2017 at 15:6 Comment(4)
@Will Not sure if it's actually a bug, which is why I'm asking. It might be expected behaviour!Crossbench
You cannot write code to modify array[0].Value directly. I think that's all about it. Couldn't you also modify it using Reflection? Span seems to be just another, indirect method in this case..Mediatory
@downvoter: It would be helpful to indicate what you think is wrong with this question, so that it can be improved.Crossbench
C# is a total crap if we trying to talk about immutability, or protecting data from unexpected modifications without extreme overhead like "full clone". It is really sad for me that C# architects ignore this concept completely.Luanneluanni
D
19

Is this behaviour correct?

Yes. A readonly struct does not change the mutability of the variable which holds a copy of the struct! Array elements are variables and variables can vary.

You don't need to use C# 7.2 to see this. Integers are immutable; there's no way to turn the integer 3 into the integer 4. Rather, you replace the contents of a variable containing 3 with 4. The fact that integers are immutable doesn't make variables into constants. Same here. The struct is immutable, just like an int, but the variable holding it is mutable.

Similarly, a readonly field on a struct is a lie; that field can be observed to change because structs do not own their storage. See Does using public readonly fields for immutable structs work? for more on this.

(And of course everything is mutable if you break the rules of the language and runtime by using reflection at a high trust level or unsafe code.)

Dorty answered 12/12, 2017 at 15:21 Comment(4)
To be able to replace content of variable with new value, you should be able to obtain that new value somehow in the first place. What if struct constructor have some limits on values it will create: public readonly struct DivisibleBy256 { public DivisibleBy256(int value) { Value = value & ~255; } public int Value { get; } }, will .AsSpan().AsBytes() respect this limits?Lyophobic
@PetSerAl: Nope. Writing bytes directly to memory is one of the most dangerous things you can do. If you don't like what happens when you do so, then my advice is: don't do it.Dorty
My main concern was: how to prevent others from doing so. But on closer inspection, it seems that whole System.Memory assembly is implicitly SecurityCritical, due to absence of any assembly level security attributes, thus it is not callable from partially trusted code.Lyophobic
@PetSerAl: C# is only mostly memory safe and type safe. The core language is pretty good at enabling the vast majority of programmers to never violate safety, but there are plenty of opportunities for fully trusted code to do so if they really want to; this is just one more.Dorty

© 2022 - 2024 — McMap. All rights reserved.