记录类型速查 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 属性 + 赋值
2Equals(R other) + IEquatable<R>所有属性用 EqualityComparer<T>.Default 比较
3== / != 运算符委托给 Equals
4GetHashCode()HashCode.Combine 所有属性
5ToString()Person { Name = Alice, Age = 30 }
6Deconstruct()按位置参数顺序解构(仅位置记录)
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

维度ClassRecord (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 使用。