为了账号安全,请及时绑定邮箱和手机立即绑定

为什么 Point.Offset() 在只读结构中没有给出编译器错误?

为什么 Point.Offset() 在只读结构中没有给出编译器错误?

C#
四季花海 2021-06-29 05:43:29
也许我误解了只读结构的概念,但我认为这段代码不应该编译: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);    } }(我使用的是 C# 7.3。)我错过了什么吗?
查看完整描述

2 回答

?
慕姐8265434

TA贡献1813条经验 获得超2个赞

编译器无法确定该Offset方法会改变Point结构成员。但是,readonly与非只读字段相比,结构字段的处理方式不同。考虑这个(非只读)结构:


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不是。现在,当您在readonlystruct 字段上调用方法时 - struct 的副本将作为this. 如果方法改变结构成员 - 比这个副本成员发生变异。出于这个原因,您没有观察到one方法被调用后的任何更改- 它没有被更改,但副本被更改了。


two字段不是只读的,并且结构本身(不是副本)被传递给Offset方法,因此如果方法改变成员 - 您可以观察到它们在方法调用后发生了变化。


因此,这是允许的,因为它不会破坏您的readonly struct. 所有字段readonly structshould be readonly,并且在 readonly struct 字段上调用的方法不能改变它,它们只能改变一个副本。


如果您对此的“官方来源”感兴趣 - 规范(7.6.4 Member access)说:


如果 T 是结构类型并且 I 标识该结构类型的实例字段:


• 如果 E 是一个值,或者如果该字段是只读的并且引用出现在声明该字段的结构的实例构造函数之外,则结果是一个值,即给定结构实例中字段 I 的值再见。


• 否则,结果是一个变量,即 E 给定的结构体实例中的字段 I。


T这里是目标类型,I是正在访问的成员。


第一部分说,如果我们在构造函数之外访问结构的 instance readonly 字段,结果是value。在第二种情况下,实例字段不是只读的 - 结果是可变的。


然后“7.5.5 函数成员调用”部分说(E这里是实例表达式,因此例如one及two以上):


• 如果 M 是在值类型中声明的实例函数成员:


如果 E 未被归类为变量,则创建 E 类型的临时局部变量并将 E 的值分配给该变量。然后将 E 重新分类为对该临时局部变量的引用。临时变量在 M 中可以这样访问,但不能以任何其他方式访问。因此,只有当 E 是一个真正的变量时,调用者才有可能观察到 M 对此所做的更改。


正如我们在上面看到的,只读结构字段访问导致 a value,而不是变量,因此E不被归类为变量。


查看完整回答
反对 回复 2021-07-03
?
狐的传说

TA贡献1804条经验 获得超3个赞

支持Evk的答案,有一篇关于Point.Offset()的文章Point's method:

请注意,调用 Offset 方法只有在您可以直接更改 X 和 Y 属性时才有效。因为 Point 是一种值类型,如果您使用属性或索引器引用 Point 对象,您将获得该对象的副本,而不是对该对象的引用。如果您尝试更改属性或索引器引用上的 X 或 Y,则会发生编译器错误。同样,在属性或索引器上调用 Offset 不会更改底层对象。如果要更改作为属性或索引器引用的 Point 的值,请创建新 Point,修改其字段,然后将该 Point 分配回属性或索引器。


根据定义Point声明为struct

最后但并非最不重要的是,Microsoft 的博客上有一篇文章描述了readonly fields,实际上struct,它们的公共属性也是只读的。

只读结构是其公共成员为只读的结构,以及“this”参数。


查看完整回答
反对 回复 2021-07-03
  • 2 回答
  • 0 关注
  • 238 浏览

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号