ref readonly 的 C# 行为
C# Behavior of ref readonly
我正在阅读有关 C# 7.2 的文档 here,我在 ref readonly
方面遇到了这个问题:
The compiler enforces that the caller can't modify the reference. Attempts to assign the value directly generate a compile-time error. However, the compiler can't know if any member method modifies the state of the struct. To ensure that the object isn't modified, the compiler creates a copy and calls member references using that copy. Any modifications are to that defensive copy.
这让我(可能还有其他人)有些困惑,所以我现在想澄清一下这个行为。假设我有一个这样定义的结构:
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
中的值被复制,因此不会改变。我的问题是关于上面提到的 "defensive copy"。为什么这是必要的? origin
中的值不能在不调用编译器错误的情况下更改,并且当 Point3D.Origin
更新时引用会正确更新,所以有什么理由要有一个额外的对象副本,这是我收集的阅读文档,没有更新?
您只能在以下情况下为 readonly
字段赋值:
在声明中初始化变量时
C# 示例:
public readonly int y = 5;
在包含实例字段声明的 class 的实例构造函数中。
在包含静态字段声明的class的静态构造函数中。
这些构造函数上下文也是唯一可以将 readonly
字段作为 out
或 ref
参数有效传递的上下文。
如果您使用如下示例的语句:
p2.y = 66; // Error
您将收到编译器错误消息:
A readonly field cannot be assigned to (except in a constructor or a
variable initializer)
ref
return 上的 readonly
修饰符表示无法修改 returned 引用。以下示例 return 是对原点的引用。它使用 readonly
修饰符来指示调用者不能修改来源:
private static readonly Point origin = new Point(0, 0);
public static ref readonly Point Origin => ref origin;
类型 returned 不需要是 readonly struct
。 return 可以被 ref
编辑的任何类型都可以被 ref readonly
编辑 return。
这些都是直接来自文档,我希望它能为您澄清!编码愉快!
因此,如果您在构造函数中设置 (1, 1, 1)
,它将 (1, 1, 1)
使用不同的方法调用更改,如上所述,您会得到 (0, 0, 0)
我正在阅读有关 C# 7.2 的文档 here,我在 ref readonly
方面遇到了这个问题:
The compiler enforces that the caller can't modify the reference. Attempts to assign the value directly generate a compile-time error. However, the compiler can't know if any member method modifies the state of the struct. To ensure that the object isn't modified, the compiler creates a copy and calls member references using that copy. Any modifications are to that defensive copy.
这让我(可能还有其他人)有些困惑,所以我现在想澄清一下这个行为。假设我有一个这样定义的结构:
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
中的值被复制,因此不会改变。我的问题是关于上面提到的 "defensive copy"。为什么这是必要的? origin
中的值不能在不调用编译器错误的情况下更改,并且当 Point3D.Origin
更新时引用会正确更新,所以有什么理由要有一个额外的对象副本,这是我收集的阅读文档,没有更新?
您只能在以下情况下为 readonly
字段赋值:
在声明中初始化变量时
C# 示例:
public readonly int y = 5;
在包含实例字段声明的 class 的实例构造函数中。
在包含静态字段声明的class的静态构造函数中。
这些构造函数上下文也是唯一可以将 readonly
字段作为 out
或 ref
参数有效传递的上下文。
如果您使用如下示例的语句:
p2.y = 66; // Error
您将收到编译器错误消息:
A readonly field cannot be assigned to (except in a constructor or a variable initializer)
ref
return 上的 readonly
修饰符表示无法修改 returned 引用。以下示例 return 是对原点的引用。它使用 readonly
修饰符来指示调用者不能修改来源:
private static readonly Point origin = new Point(0, 0);
public static ref readonly Point Origin => ref origin;
类型 returned 不需要是 readonly struct
。 return 可以被 ref
编辑的任何类型都可以被 ref readonly
编辑 return。
这些都是直接来自文档,我希望它能为您澄清!编码愉快!
因此,如果您在构造函数中设置 (1, 1, 1)
,它将 (1, 1, 1)
使用不同的方法调用更改,如上所述,您会得到 (0, 0, 0)