记录类型速查 Records Cheatsheet
C# 9+ · 不可变引用类型 · 值相等语义
声明语法
| 形式 | 语法 | 编译器生成 |
| 位置记录 Positional |
record Person(string Name, int Age); |
属性 + 构造 + Equals + GetHashCode + ToString + Deconstruct + <Clone>$ |
| 命名记录 Nominal |
record Person { public string Name { get; init; } } |
Equals + GetHashCode + ToString + <Clone>$(不含构造和 Deconstruct) |
| 密封记录 |
sealed record Point(int X, int Y); |
同上,阻止继承 |
值相等 Value Equality 语义
var a = new Person("A", 30);
var b = new Person("A", 30);
a == b // True ← class 则是 False
a.Equals(b) // True
ReferenceEquals(a, b) // False —— 不同的堆对象
编译器生成的 7 个方法
| # | 方法 | 行为 |
| 1 | 构造函数 + init 属性 | 位置参数 → init-only 属性 + 赋值 |
| 2 | Equals(R other) + IEquatable<R> | 所有属性用 EqualityComparer<T>.Default 比较 |
| 3 | == / != 运算符 | 委托给 Equals |
| 4 | GetHashCode() | HashCode.Combine 所有属性 |
| 5 | ToString() | Person { Name = Alice, Age = 30 } |
| 6 | Deconstruct() | 按位置参数顺序解构(仅位置记录) |
| 7 | <Clone>$() | MemberwiseClone() 浅拷贝——with 的底层 |
With 表达式 With Expressions
var next = original with { Prop = newValue };
// ① 调用 <Clone>$() 创建浅拷贝
// ② 给指定属性赋新值
// ③ 返回新对象——原对象不变
Init-only 属性 Init-only Properties
public string Name { get; init; } // init = 只能在构造 / 初始化器 / with 中赋值
var p = new Person { Name = "Bob" }; // ✅ 初始化器
var p2 = p with { Name = "Alice" }; // ✅ with 表达式
p.Name = "Charlie"; // ❌ CS8852 编译错误
Record vs Class vs Struct
| 维度 | Class | Record (C# 9) | Struct |
| 类型 | 引用类型 | 引用类型 | 值类型 |
| 相等语义 | 引用相等 | 值相等 | 值相等(反射) |
| 可变性 | 默认可变 | 默认不可变 | 默认可变 |
| 继承 | ✅ | ✅(仅 record ↔ record) | ❌ |
| 样板代码 | 手写 | 编译器生成 | 手写 |
| 典型用途 | 实体、服务 | DTO、值对象、消息 | 小数据容器 |
继承规则
| 场景 | 允许 |
| Record → Record | ✅ |
| Record → Class | ❌ |
| Class → Record | ❌ |
sealed record | ✅ 阻止继承 |
注意事项
⚠ With 做的是浅拷贝——嵌套引用类型属性共享同一实例。修改嵌套对象要手动用嵌套 with。
⚠ 集合属性小心值相等——List<T> 不支持值相等。用 ImmutableList<T> 或 IReadOnlyList<T>。
⚠ 继承 + 值相等——编译器插入 EqualityContract 属性,确保 Student 和 Teacher(即使属性相同)不相等。
⚠ EF Core 实体的局限——主实体建议用 class(需要变更追踪),从 EF Core 6 开始 Record 可作为 Owned Entity 使用。