readonly struct 是值类型且实例不可变,字段仅能在构造函数或初始化器中赋值;readonly 成员则约束方法/属性不修改状态,二者配合可强化不可变性与性能优化。

readonly struct 是值类型,不能被修改
在 C# 中,readonly struct 表示整个结构体实例是不可变的:编译器会阻止对任何字段(包括 private 字段)的赋值,除非发生在构造函数或字段初始化器中。它不等于“只读字段”,而是整个实例层面的写保护。
常见错误现象:CS8342 “Cannot assign to field 'xxx' because it is a readonly field of a readonly variable” —— 这通常出现在你试图修改 readonly struct 的字段,或者通过 ref readonly 返回后又尝试赋值时。
- 必须用
public readonly或init(C# 9+)声明所有可访问字段;私有字段也得是readonly,否则编译失败 - 构造函数里可以赋值,但之后所有路径(包括属性 setter、方法内部)都不能改字段
- 不能包含
ref返回字段的方法,也不能有可变的get属性(比如返回ref int) - 性能影响小:编译器可能做内联优化,且避免了防御性拷贝(尤其传参时)
public readonly struct Point
{
public readonly int X;
public readonly int Y;
public Point(int x, int y) => (X, Y) = (x, y);
// ❌ 编译错误:无法在 readonly struct 中定义 set 访问器
// public int X { get; set; }}
readonly 成员(readonly member)是方法/属性级的只读约束
readonly 修饰符加在方法、属性 getter、索引器或运算符上,表示该成员不会修改当前实例的状态。它和 readonly struct 配合使用效果最好,但也能用于普通 struct 或 class(class 中仅作语义提示,无强制约束)。
使用场景:当你想明确表达“这个方法只是读数据”,同时让编译器帮你检查是否意外写了字段;或配合 in 参数提升性能。
- 在
readonly struct中,所有成员默认应为readonly;若漏加,编译器会报CS8656:“调用非 readonly 成员会丢弃 readonly 状态” - 在普通
struct中加readonly方法,能安全地被readonly变量调用(否则会报错) - 不能在
readonly方法中给任何实例字段赋值,也不能调用非readonly成员 - 属性
get可以加readonly,但set不允许(语法不允许)
public readonly struct Rectangle
{
public readonly double Width;
public readonly double Height;
public Rectangle(double w, double h) => (Width, Height) = (w, h);
// ✅ 正确:readonly 方法,只读字段,不修改状态
public readonly double Area => Width * Height;
// ✅ 正确:显式 readonly getter
public readonly double Perimeter => 2 * (Width + Height);
}
readonly 字段 vs readonly 成员:别混淆作用域
readonly 字段(如 private readonly int _value;)控制的是字段本身能否被重新赋值;而 readonly 成员控制的是「调用者能否通过这个成员改变实例」——两者粒度不同,常一起出现,但目的分离。
容易踩的坑:readonly 字段仍可被其类型内部修改(比如字段是 StringBuilder,你在 readonly 方法里调 .Append() 是合法的),这会让 readonly 成员失去意义。所以真正安全的只读需要类型本身也是不可变的(如 string、int、自定义 readonly struct)。
-
readonly字段可在构造函数、声明时初始化;readonly成员只能加在方法、属性get、索引器等成员上 - 字段是存储层面的约束,成员是行为层面的契约
- 对引用类型字段,
readonly只锁住“引用不变”,不锁住“对象内容不变” - 如果 struct 包含可变引用类型字段(如
List),即使加了readonly成员,也无法保证逻辑只读
实际使用建议:从 readonly struct 开始,逐步收紧
不要一上来就给所有 struct 加 readonly;先确认它确实代表一个不可变概念(如坐标、颜色、时间区间)。然后补全 readonly 成员,并用 in 参数传递它以避免复制开销。
- 优先用
readonly struct替代普通struct,尤其是作为 DTO、数学类型或高频传参场景 - 所有公开字段声明为
public readonly;私有字段也加readonly(编译器要求) - 每个方法、属性
get都显式加readonly,除非你真需要修改状态(那它就不该是readonly struct) - 搭配
in参数使用:void Process(in Rectangle r),此时编译器会确保只调用readonly成员 - 注意兼容性:C# 7.2+ 才支持
readonly struct,旧项目升级需检查语言版本
最易被忽略的一点:readonly 不是线程安全的银弹。它只防止意外修改,不提供同步保障;多线程读同一个 readonly struct 没问题,但若底层字段是共享可变对象(比如缓存的 Lazy),仍需额外同步。









