У компилятора нет возможности выяснить, что метод Offset
мутирует Point
членов структуры.Однако readonly
поле struct обрабатывается по-другому, чем не только для чтения.Рассмотрим эту (не только для чтения) структуру:
public struct TwoPoints {
private readonly Point one;
private Point two;
public void Foo() {
one.Offset(5, 5);
Console.WriteLine(one.X); // 0
two.Offset(5, 5);
Console.WriteLine(two.X); // 5
}
}
one
поле доступно только для чтения, а two
- нет.Теперь, когда вы вызываете метод в readonly
поле структуры - копия структуры передается этому методу как this
.И если метод мутирует членов структуры - тогда члены этой копии мутируют.По этой причине вы не наблюдаете никаких изменений в one
после вызова метода - он не был изменен, но копия была.
two
поле не только для чтения, а сама структура (не копия)передан методу Offset
, и поэтому, если метод мутирует члены - вы можете наблюдать их изменение после вызова метода.
Итак, это разрешено, потому что это не может нарушить контракт неизменности вашего readonly struct
.Все поля readonly struct
должны быть readonly
, а методы, вызываемые в поле readonly struct, не могут изменить его, они могут только изменять копию.
Если вас интересует «официальный источник» для этой спецификации (7.6.4 Доступ к элементу) говорит, что:
Если T является структурным типом, и я идентифицирую поле экземпляра этого структурного типа:
• Если E является значением,или если поле доступно только для чтения и ссылка находится за пределами конструктора экземпляра структуры, в которой объявлено поле, то результатом является значение, а именно значение поля I в экземпляре структуры, заданное E.
• В противном случае результатом является переменная, а именно поле I в экземпляре структуры, заданном E.
T
здесь - это тип цели, а I
- доступ к члену.
Первая часть говорит, что если мы получим доступ к полю readonly экземпляра структуры, вне конструктора, результат будет value .Во втором случае, когда поле экземпляра не доступно только для чтения - результат равен переменная .
Тогда в разделе «7.5.5 Вызов члена функции» говорится (E
здесь выражение экземпляра, например,one
и two
выше):
• Если M является элементом функции экземпляра, объявленным в типе значения:
Если E не классифицируется как переменная, тосоздается временная локальная переменная типа E, и этой переменной присваивается значение E.Затем E переклассифицируется как ссылка на эту временную локальную переменную.Временная переменная доступна как это в пределах M, но никаким другим способом.Таким образом, только когда E является истинной переменной, вызывающая сторона может наблюдать изменения, которые вносит в нее М.
Как мы видели выше, доступ к полям struct readonly struct приводит к value
,не переменная, поэтому E
не классифицируется как переменная.