4

也许我误解了只读结构的概念,但我认为这段代码不应该编译:

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。)我错过了什么吗?

4

2 回答 2

6

编译器无法确定该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不是。readonly现在,当您在struct 字段上调用方法时- struct 的副本将作为this. 如果方法改变了结构成员——那么这个副本成员就会改变。出于这个原因,您不会观察到oneafter 方法被调用的任何变化——它没有被改变,但副本被改变了。

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

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

如果您对此的“官方来源”感兴趣 - 规范(7.6.4 会员访问)说:

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

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

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

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

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

然后“7.5.5 函数成员调用”部分说(E这里是实例表达式,例如onetwo上面):

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

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

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

于 2018-05-25T11:03:08.410 回答
2

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

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


根据定义Point声明为struct

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

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

于 2018-05-25T10:59:11.380 回答