.NET10之Record 深度解析

一、基本功能介绍

Record 是C# 9.0引入的类型修饰符,用于创建数据驱动的类型,编译器自动合成大量成员以简化数据模型开发。分为两种核心类型:

类型 声明语法 本质 特性
Record Class recordrecord class 引用类型 位置参数生成init-only属性,默认不可变
Record Struct record struct 值类型 位置参数生成读写属性,支持值语义
核心语法示例
csharp 复制代码
// 1. 位置记录(最常用)
public record Person(string FirstName, string LastName);

// 2. 带自定义成员的记录
public record Person
{
    public required string FirstName { get; init; }
    public required string LastName { get; init; }
    public int Age { get; init; } = 18; // 默认值
}

// 3. 只读值类型记录
public readonly record struct Point(double X, double Y, double Z);
编译器自动合成的核心成员
  1. 主构造函数:与位置参数匹配的构造函数
  2. 属性 :位置参数对应的公共属性(record class为init-only,record struct为读写)
  3. 值相等性Equals(T other)==/!=运算符,基于属性值比较
  4. 解构方法Deconstruct(out T1 prop1, out T2 prop2, ...)
  5. ToString:格式化字符串,显示类型名和属性值
  6. 复制构造函数 :支持with表达式的非破坏性复制

二、设计原理深度剖析

1. 核心设计目标
  • 简化数据模型:减少样板代码,专注数据定义而非成员实现
  • 值语义优先:基于值而非引用比较相等性,适合数据传输场景
  • 不可变友好:默认不可变,提升线程安全和可预测性
  • 继承支持:记录可继承自其他记录,构建层次化数据模型
2. 与类/结构体的关键区别
特性 Record Class Class Record Struct Struct
相等性 值相等(基于属性) 引用相等 值相等(基于属性) 值相等(基于反射)
位置参数 支持,生成init-only属性 不支持 支持,生成读写属性 不支持
继承 可继承自其他记录 可继承自任意类 可继承自其他记录结构体 不可继承
默认可变性 不可变(init-only) 可变 可变(读写) 可变
解构方法 自动生成 需手动实现 自动生成 需手动实现
3. 不可变性与非破坏性复制
  • 浅不可变性record class的位置参数属性为init-only,初始化后不可直接修改
  • 引用类型的浅不可变限制:引用类型属性的内部数据仍可修改(如数组元素)
  • 非破坏性复制 :通过with表达式创建新实例,复制原实例并修改指定属性
csharp 复制代码
Person person1 = new("Nancy", "Davolio");
Person person2 = person1 with { FirstName = "John" }; // 复制并修改
4. 继承与相等性契约
  • 继承规则:记录只能继承自其他记录,类不能继承自记录,反之亦然
  • EqualityContract :编译器合成protected override Type EqualityContract { get; },确保派生记录的相等性比较基于运行时类型
  • ToString格式化 :基类PrintMembers方法被调用,包含所有基类和派生类的属性

三、生产环境使用场景

1. 最佳适用场景
  1. 数据传输对象(DTO):API请求/响应、消息队列数据,简化序列化/反序列化
  2. 领域模型:值对象、不可变的业务实体,确保数据一致性
  3. 配置模型:应用配置、系统参数,支持不可变配置加载
  4. 状态管理:函数式编程中的状态对象,避免副作用
  5. 测试数据:单元测试中的测试数据对象,简化数据构建
2. 不适用场景
  1. 实体框架核心(EF Core)实体:依赖引用相等性,记录不适合
  2. 频繁修改的对象:如业务实体的状态频繁变更,类更适合
  3. 需要自定义相等性逻辑:需覆盖合成的相等性方法,增加复杂度
  4. 性能敏感的高频创建对象:记录的合成成员有轻微性能开销
3. 企业级实践示例

场景1:API响应模型

csharp 复制代码
// 定义响应记录
public record ApiResponse<T>(int StatusCode, string Message, T Data);

// 使用
var response = new ApiResponse<User>(200, "Success", new User { Id = 1, Name = "John" });
Console.WriteLine(response); // 自动格式化输出

场景2:分层数据模型

csharp 复制代码
// 基记录
public abstract record Entity(int Id);

// 派生记录
public record User(int Id, string Name, string Email) : Entity(Id);
public record Product(int Id, string Name, decimal Price) : Entity(Id);

// 相等性验证
var user1 = new User(1, "John", "john@example.com");
var user2 = new User(1, "John", "john@example.com");
Console.WriteLine(user1 == user2); // True(值相等)

场景3:非破坏性复制

csharp 复制代码
// 订单记录
public record Order(int Id, string CustomerName, decimal Total, DateTime OrderDate);

// 创建订单
var order = new Order(1, "Nancy Davolio", 99.99m, DateTime.Now);

// 修改部分属性创建新订单
var updatedOrder = order with { Total = 129.99m, OrderDate = DateTime.Now.AddDays(1) };

四、版本演进与最新特性(2026)

  • C# 10 :引入record struct,支持值类型记录
  • C# 12:优化记录性能,减少内存开销
  • C# 14:改进记录的泛型支持和编译器优化
  • C# 15(预览):增强记录的模式匹配和与联合类型的集成

五、总结与最佳实践

  1. 选择建议
    • 优先使用record class定义引用类型数据模型
    • 性能敏感的值类型场景使用record struct
    • 简单数据结构优先考虑位置记录语法
  2. 性能考量
    • 高频创建的轻量级数据对象考虑使用结构体
    • 大型记录结构注意内存占用,合理拆分
  3. 可维护性
    • 为复杂记录添加XML文档注释
    • 统一记录命名规范,便于团队协作
    • 避免过度继承,保持层次简单
相关推荐
唐青枫13 小时前
别再层层传参了!C#.NET AsyncLocal 异步上下文透传实战
c#·.net
小邓的技术笔记16 小时前
.NET 10 使用 Microsoft.AspNetCore.OpenApi 实现 API 版本管理
.net
夏霞17 小时前
IIS 应用程序池 3 种标识:ApplicationPoolIdentity / LocalSystem / LocalService 权限区别(超清晰)
c#·.net
回忆2012初秋1 天前
Quartz.NET 全面解析与实战指南
.net
我是唐青枫2 天前
C#.NET ThreadLocal 深入解析:线程独享数据、性能收益与实战边界
c#·.net
唐青枫2 天前
别再把增删改查写成一锅粥!C#.NET CQRS 从原理到实战
c#·.net
唐青枫2 天前
C#.NET ThreadLocal 深入解析:线程独享数据、性能收益与实战边界
c#·.net
SEO-狼术2 天前
Include Scannable Barcodes in Reports
.net
qq_431280703 天前
工作经验总结:半导体上位机软件开发与互联网开发的不同
c#·.net
ironinfo3 天前
.net 高并发服务性能瓶颈排查处理
性能优化·.net·grpc