C#-record

310 阅读3分钟

🌟 什么是记录(record)?

记录是C# 9引入的一种特殊类型,专门用来处理数据模型。它像是一个自带"数据专用"功能的类或结构体(record classrecord 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 }

⚠️ 使用记录的注意事项

  1. 不要用记录替代所有类: • 适合场景:DTO(数据传输对象)、配置项、不可变数据模型 • 不适合场景:EF Core实体(依赖引用相等性)、需要频繁修改的对象

  2. 引用类型属性的陷阱

    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响应模型) • ✅ 需要不可变数据 • ✅ 想减少样板代码(自动生成EqualsGetHashCode等) • ❌ 避免用于需要频繁修改的对象 • ❌ 避免用于EF Core实体

记录是处理数据模型的利器,但要根据场景选择是否使用。就像螺丝刀和锤子——用对工具才能事半功倍!