You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# 9 record与带自动实现只读属性的类的不可变性差异

带只读自动属性的类与C# 9 Record的不可变性对比

基础不可变性的共性

你给出的两个示例在公开属性的不可变性层面没有本质区别:

  • 普通类的{ get; }只读自动属性,仅能在类的构造函数中赋值,实例构造完成后外部无法修改
  • 默认声明的位置参数Record,编译器会为所有参数生成带init访问器的公开属性,仅能在实例构造阶段(构造函数、对象初始化器)赋值,构造完成后外部无法修改
    两者底层都由编译器生成私有只读支持字段,公开层面都满足不可变数据对象的契约。

不可变性相关的核心差异

1. 实例拷贝能力

Record由编译器自动生成with表达式支持,可以快速基于现有实例生成修改了指定属性的新不可变实例:

var oldColor = new RgbColor(255,0,0);
var newColor = oldColor with { Green = 255 }; // 生成新实例,原实例不受影响

普通只读类要实现相同能力,需要手动编写拷贝构造函数、属性拷贝逻辑,开发成本更高。

2. 反序列化兼容性

两者反序列化完成后都具备完全的不可变性,不会被外部修改,但反序列化过程的兼容性存在差异:

  • 普通只读类的属性仅能在构造函数中赋值,要求反序列化库必须严格匹配构造函数的参数名称、类型和数量,否则会直接反序列化失败
  • Record的init访问器允许反序列化库在构造实例后再完成属性赋值,对不同反序列化配置的兼容性更好,不会破坏最终的不可变性

Record针对不可变场景的额外增强

除了语法更简洁之外,Record默认提供了很多针对不可变数据场景的优化:

  • 默认值相等性:两个Record实例只要所有属性值完全一致,就会被判定为相等,不需要手动重写Equals、GetHashCode方法,非常适合不可变数据作为字典键、集合元素的场景
  • 编译器层面的不可变契约保障:使用位置参数声明Record时,编译器默认生成init属性,不会像手动编写普通类那样,意外漏掉只读声明引入可变属性
  • 自动生成结构化的ToString()方法:默认会输出所有属性的键值对,方便调试不可变对象的状态

注意:Record本身不强制不可变性,如果你手动为属性添加set访问器,也可以实现可变的Record,只有默认位置参数声明的Record才会默认满足不可变契约。


内容的提问来源于stack exchange,提问作者andrei.ciprian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 11:36:03