我正在阅读有关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
更新时,引用也会正确更新,因此,有什么理由要获得该对象的额外副本,这是由我收集的阅读文档,是否未更新?
答案 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)