C#ref只读行为

时间:2018-10-24 18:30:12

标签: c# ref c#-7.2

我正在阅读有关C#7.2 here的文档,并且在ref readonly方面遇到了这一问题:

  

编译器强制调用者不能修改引用。尝试直接分配该值会产生编译时错误。但是,编译器无法知道是否有任何成员方法修改了结构的状态。为了确保不修改该对象,编译器将创建一个副本并使用该副本调用成员引用。任何修改都针对该防御性副本。

这给我(可能还有其他一些人)带来了一些困惑,所以我现在想澄清一下行为。假设我有一个这样定义的结构:

public struct Point3D
{
    private static Point3D origin = new Point3D(0,0,0);

    public static ref readonly Point3D Origin => ref origin;

    public int X { get; set; }
    public int Y { get; set; }
    public int Z { get; set; }

    public static void ChangeOrigin(int x = 0, int y = 0, int z = 0)
    {
        origin = new Point3D(x, y, z);
    }
}

现在,假设我使用ref readonly来获取Point3D.Origin并对其进行了修改:

ref readonly var origin = ref Point3D.Origin;
var originValue = Point3D.Origin;
Point3D.ChangeOrigin(1, 1, 1);

Console.WriteLine("Origin is: ({0}, {1}, {2})", origin.X, origin.Y, origin.Z);
Console.WriteLine("Origin is: ({0}, {1}, {2})", originValue.X, originValue.Y, originValue.Z);

运行此代码的结果是:

Origin is: (1, 1, 1)
Origin is: (0, 0, 0)

这是预期的。当我调用origin时,ChangeOrigin中的值会更新,而originValue中的值将被复制,因此不会更改。我的问题是关于上述“防御性副本”。为什么这是必要的? origin中的值必须在不调用编译器错误的情况下进行更改,并且在Point3D.Origin更新时,引用也会正确更新,因此,有什么理由要获得该对象的额外副本,这是由我收集的阅读文档,是否未更新?

1 个答案:

答案 0 :(得分:1)

仅在以下情况下,您可以将值分配给只读字段:

  • 在声明中初始化变量时。

    C#示例:

    public readonly int y = 5;
    
  • 在包含实例字段声明的类的实例构造函数中。

  • 在包含静态字段声明的类的静态构造函数中。

这些构造函数上下文也是唯一可以将只读字段作为out或ref参数传递的上下文。

如果使用类似以下示例的语句:

p2.y = 66; // Error

您将收到编译器错误消息:

  

不能将只读字段分配给(除非在构造函数或   变量初始值设定项)

引用返回的readonly修饰符表示不能修改返回的引用。以下示例返回对原点的引用。它使用readonly修饰符表示呼叫者无法修改来源:

private static readonly Point origin = new Point(0, 0);
public static ref readonly Point Origin => ref origin;

返回的类型不必是只读结构。 ref可以返回的任何类型都可以由ref readonly返回的类型

这完全来自DOC,我希望它能为您澄清一下!编码愉快!

因此,如果您在构造函数中设置(1,1,1),则将(1,1,1)用其他方法调用更改,如上所述,您将获得(0,0,0)