Eri*_*ers 4 .net c# struct readonly-attribute
也许我误解了readonly结构的概念,但我认为这段代码不应该编译:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
(我正在使用C#7.3.)我错过了什么?
编译器无法确定该Offset方法是否会改变Pointstruct成员.但是,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
}
}
Run Code Online (Sandbox Code Playgroud)
one字段是只读但two不是.现在,当您在readonlystruct字段上调用方法时- 将 struct 的副本传递给该方法this.如果方法改变了struct成员 - 那么这个副本成员就会发生变异.出于这个原因,你没有观察到one调用after方法的任何更改- 它没有被更改但是复制了.
twofield不是readonly,struct本身(不是copy)传递给Offset方法,因此如果方法改变成员 - 你可以观察它们在方法调用后改变.
所以,这是允许的,因为它不能破坏你的不变性合同readonly struct.readonly struct应该是所有字段readonly,并且readonly struct字段上调用的方法不能改变它,它们只能改变副本.
如果您对"官方来源"感兴趣 - 规范(7.6.4会员访问)说:
如果T是struct-type并且我标识了该struct-type的实例字段:
•如果E是一个值,或者该字段是readonly并且引用发生在声明该字段的struct的实例构造函数之外,那么结果是一个值,即给定struct结构中的字段I的值由E.
•否则,结果是一个变量,即E给出的struct实例中的字段I.
T这是目标类型,I是被访问的成员.
第一部分说如果我们在构造函数之外访问结构的实例readonly字段,结果就是值.在第二种情况下,实例字段不是只读的 - 结果是可变的.
然后部分"7.5.5函数成员调用"表示(E这里是实例表达式,因此,例如one与two以上):
•如果M是在value-type中声明的实例函数成员:
如果E未被归类为变量,则创建E类型的临时局部变量,并将E的值分配给该变量.然后将E重新分类为对该临时局部变量的引用.临时变量在M中可以访问,但不能以任何其他方式访问.因此,只有当E是真变量时,呼叫者才有可能观察到M对此做出的变化.
正如我们在上面看到的那样,readonly struct field access导致a value而不是变量,因此E不被归类为变量.