【发布时间】:2019-03-29 06:27:10
【问题描述】:
我正在阅读有关 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 更新时正确更新,所以有什么理由需要对象的额外副本,这是我通过阅读文档收集的内容,没更新?
【问题讨论】:
-
您要求我们告诉您这段代码的结果是什么?你不能通过运行代码找到它吗?我们是人,不是计算机。
-
@mason 我之所以问这个问题,部分原因是我对语言功能感到好奇,还没有升级到 7.2,部分原因是我认为有人会比我更了解它。另外,因为我认为这对将来对此有疑问的人会有所帮助
-
@mason 是的,我确实可以运行代码。但这并不能告诉我为什么,代码以某种方式运行,现在可以了。这就是提出这个问题的真正动力。而且,如果你知道为什么代码会这样运行,那么你就不需要运行它了。
-
@mason 简而言之,我对代码的作用不太好奇,而对代码的行为方式为何如此好奇
-
与 SpinLock 相关的 github 问题。 github.com/dotnet/roslyn/issues/17310