Perhaps I'm misunderstanding the concept of a readonly struct, but I would think this code should not compile:
public readonly struct TwoPoints
{
private readonly Point one;
private readonly Point two;
void Foo()
{
// compiler error: Error CS1648 Members of readonly field 'TwoPoints.one'
// cannot be modified (except in a constructor or a variable initializer)
one.X = 5;
//no compiler error! (and one is not changed)
one.Offset(5, 5);
}
}
(I'm using C# 7.3.) Am I missing something?
There is no way for compiler to figure out that
Offset
method mutatesPoint
struct members. However,readonly
struct field is handled differently compared to non-readonly one. Consider this (not readonly) struct:one
field is readonly buttwo
is not. Now, when you call a method onreadonly
struct field - a copy of struct is passed to that method asthis
. And if method mutates struct members - than this copy members are mutated. For this reason you don't observe any changes toone
after method is called - it has not been changed but copy was.two
field is not readonly, and struct itself (not copy) is passed toOffset
method, and so if method mutates members - you can observe them changed after method call.So, this is allowed because it cannot break immutability contract of your
readonly struct
. All fields ofreadonly struct
should bereadonly
, and methods called on readonly struct field cannot mutate it, they can only mutate a copy.If you are interested in "official source" for this - specification (7.6.4 Member access) says that:
T
here is target type, andI
is member being accessed.First part says that if we access instance readonly field of a struct, outside of constructor, the result is value. In second case, where instance field is not readonly - result is variable.
Then section "7.5.5 Function member invocation" says (
E
here is instance expression, so for exampleone
andtwo
above):As we saw above, readonly struct field access results in a
value
, not a variable and so,E
is not classified as variable.