🌟 什么是记录(record)?
记录是C# 9引入的一种特殊类型,专门用来处理数据模型。它像是一个自带"数据专用"功能的类或结构体(record class或record struct),核心特点是:值相等性和不可变性。
🔍 值相等性 vs 引用相等性
• 类的痛点:两个类对象,即使属性完全相同,只要不是同一个内存地址,==比较会返回false。
class PersonClass { string Name; }
var a = new PersonClass { Name = "小明" };
var b = new PersonClass { Name = "小明" };
Console.WriteLine(a == b); // false ❌
• 记录的亮点:记录会逐个比较所有属性的值是否相等。
record PersonRecord(string Name);
var a = new PersonRecord("小明");
var b = new PersonRecord("小明");
Console.WriteLine(a == b); // true ✅
就像身份证号:两个人身份证号相同,就是同一个人,不管是不是复制出来的。
🔒 不可变性(Immutability)
记录默认生成只读属性(类似init),对象一旦创建,属性就不能修改:
var person = new PersonRecord("小明");
person.Name = "小红"; // 编译错误!🚫
为什么需要不可变? • 线程安全:多线程环境下,数据不会被意外修改 • 哈希表安全:对象的哈希码在生命周期内不会变化
🛠 记录的特殊技能
1. 位置参数(Positional Parameters)
声明时直接定义属性:
record Student(string Name, int Age); // 自动生成Name和Age属性
等价于手动写一个包含构造函数、属性和相等性比较的类,省去大量样板代码。
2. with表达式:快速克隆
想修改记录中的某个属性?用with生成新对象:
var student1 = new Student("小明", 18);
var student2 = student1 with { Age = 19 }; // 新对象,Age变为19
3. 友好的ToString()
自动生成易读的字符串表示:
Console.WriteLine(student1);
// 输出:Student { Name = 小明, Age = 18 }
⚠️ 使用记录的注意事项
-
不要用记录替代所有类: • 适合场景:DTO(数据传输对象)、配置项、不可变数据模型 • 不适合场景:EF Core实体(依赖引用相等性)、需要频繁修改的对象
-
引用类型属性的陷阱:
record Data(string[] Tags); var a = new Data(new[] { "A", "B" }); var b = a with { }; a.Tags[0] = "C"; Console.WriteLine(b.Tags[0]); // 输出"C"!因为数组是引用类型值相等性只比较引用地址,不会深比较引用类型的内容。
🆚 记录 vs 类 vs 结构体
| 特性 | 记录 (record) | 类 (class) | 结构体 (struct) |
|---|---|---|---|
| 相等性比较 | 值相等 | 引用相等 | 值相等(按字段) |
| 不可变性 | 默认支持 | 需手动实现 | 需手动实现 |
with表达式 | ✅ | ❌ | ❌ |
| 继承 | 只能继承记录 | 支持类继承 | 不能继承 |
🌰 经典示例解析
示例1:基本使用
public record Person(string FirstName, string LastName);
var person = new Person("Nancy", "Davolio");
Console.WriteLine(person);
// 输出:Person { FirstName = Nancy, LastName = Davolio }
亮点:自动生成构造函数、属性、ToString()。
示例2:值相等性
var phoneNumbers = new string[2];
var person1 = new Person("Nancy", "Davolio", phoneNumbers);
var person2 = new Person("Nancy", "Davolio", phoneNumbers);
Console.WriteLine(person1 == person2); // true ✅
person1.PhoneNumbers[0] = "555-1234";
Console.WriteLine(person1 == person2); // 仍然true!因为比较的是数组引用
注意:记录不会深比较引用类型的内容,只比较引用地址是否相同。
💡 总结:何时使用记录?
• ✅ 需要值语义(比如配置对象、API响应模型)
• ✅ 需要不可变数据
• ✅ 想减少样板代码(自动生成Equals、GetHashCode等)
• ❌ 避免用于需要频繁修改的对象
• ❌ 避免用于EF Core实体
记录是处理数据模型的利器,但要根据场景选择是否使用。就像螺丝刀和锤子——用对工具才能事半功倍!