.NET10之Record 深度解析

一、基本功能介绍

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

类型 声明语法 本质 特性
Record Class record 或 record 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文档注释
    • 统一记录命名规范,便于团队协作
    • 避免过度继承,保持层次简单
相关推荐
波力海苔夹心脆67513 小时前
C# 视觉检测实战:PLC 按钮触发 VisionPro 检测,OK 亮灯 / NG 灭灯(西门子 PLC,含界面显示与图片存档)
开发语言·经验分享·c#·自动化·视觉检测·.net
hez201014 小时前
一行代码在 .NET 项目里启用 Satori GC
c#·.net·.net core·gc·clr
QUDONG_biubiubiu18 小时前
.NET Runtime 6.0/7.0/8.0/9.0下载修复教程,软件打不开这样解决
.net·net runtime下载·net运行库修复·net runtime安装
宝桥南山18 小时前
.NET 11 - 尝试创建一下AGUIServer和AGUIClient(基于新版.NET SDK for AG-UI)
microsoft·ai·微软·c#·aigc·.net
波力海苔夹心脆67518 小时前
C# LINQ 入门到上手:一篇讲透查询语法、常用操作符与延迟执行(含示例与速查表
经验分享·c#·.net·solr·linq
小羊没烦恼!2 天前
Windows Azure Platform体验(1):Windows Azure
java·大数据·后端·python·flask·word·.net
CSharp精选营3 天前
IIS 后面挂 Kestrel,这 3 个配置错了就 502
kestrel·.net·iis配置
鑫 源3 天前
C#源码开源, 获取macOS 电池数据,包括iOS (xamarin开发)
c#·.net·xamarin
xiangcns4 天前
PDMS PiplineTool(三十)一堆小优化+一个彩蛋功能
.net·二次开发·pdms