为何readonly struct中调用Point.Offset()未触发编译器错误?
问题解答
你确实忽略了C#中值类型与readonly字段交互的几个关键细节,咱们一步步拆解清楚:
1. 为什么one.X = 5会触发编译错误?
当你声明private readonly Point one;时,one是一个**readonly修饰的值类型字段**。C#编译器会严格禁止直接修改readonly值类型字段的成员——因为readonly字段的整个实例在构造完成后就不允许被修改,直接修改成员等同于修改字段实例本身,这违反了readonly的约束,因此触发CS1648错误。
2. 为什么one.Offset(5, 5)不报错,且原字段值毫无变化?
这里的核心是值类型的复制特性:
- 当你访问
readonly值类型字段one时,编译器会自动创建该字段的临时副本(值类型的访问/传递都是按值进行的)。 - 你调用的
Offset方法是在这个临时副本上执行的,方法内部修改的是副本的X和Y值,完全不会触及原readonly字段one的实例。 - 由于你没有直接修改
readonly字段的成员,只是对一个临时的、非readonly的副本调用方法,所以编译器不会阻止这个操作。但临时副本在方法执行后就会被丢弃,原字段自然不会有任何变化。
补充:readonly struct 和 readonly字段的区别
顺便提一句,你声明的readonly struct TwoPoints是指整个结构体实例不可变(所有字段默认readonly,且不允许存在修改成员的方法),但这里的核心矛盾是内部readonly Point one字段的行为——哪怕TwoPoints不是readonly struct,只要one是readonly值类型字段,上述现象依然会发生。
如果想让类似Offset的操作真正生效,你可以把one改为非readonly字段(但会破坏不可变性),或者采用值类型的不可变设计模式:
// C# 7.3需要手动创建新实例 one = new Point(one.X + 5, one.Y + 5);
内容的提问来源于stack exchange,提问作者Erik Bongers
相关产品推荐
相关产品推荐

