record 是 c# 9.0 引入的不可变引用类型,编译器自动生成值语义的 equals/gethashcode、结构化 tostring、with 表达式及位置构造;它不提供序列化、日志记录或持久化能力,需手动实现。

record 是 C# 9.0 引入的引用类型,天生适合做不可变数据载体;它不是“记录日志”的 record,也不会自动写入文件或数据库——想存日志,得自己调用 File.WriteAllText 或 ILogger.Log。
record 关键字到底生成了什么
用 record 定义类型时,编译器会自动生成:值语义的 Equals/GetHashCode、ToString 的结构化输出、with 表达式支持、位置语法(record Person(string Name, int Age))对应的构造与只读属性。它不生成任何序列化逻辑、不触发事件、不写磁盘。
- 所有字段默认是
init(仅初始化赋值),不能在构造后修改 -
with创建的是新实例,原实例不变 —— 这是不可变性的核心表现 - 若手动添加可变字段(如
public string Tag { get; set; }),会破坏值相等性的一致性,Equals仍只比较自动生成的属性
想把 record 实例保存到文件,必须显式序列化
record 本身不提供持久化能力。常见做法是用 System.Text.Json 序列化为 JSON 再写入文件:
var user = new User("Alice", 30);
string json = JsonSerializer.Serialize(user);
File.WriteAllText("user.json", json);
注意点:
- 确保类成员是 public 且有 getter(
record自动生成的属性满足) - 嵌套 record 或集合能正常序列化,但含循环引用会抛
InvalidOperationException - 若字段名含下划线(如
Record_C),默认 JSON 键名也是Record_C;需用[JsonPropertyName("recordC")]控制命名 - 别依赖
ToString()输出当数据格式——它仅供调试,结构不保证稳定
record 和日志框架(如 Microsoft.Extensions.Logging)怎么配合
日志框架记录的是字符串消息,不是对象本身。要把 record 内容写进日志,得先转成可读文本:
- 直接插值:
logger.LogInformation("User: {@User}", user)——{@User}触发结构化日志(Serilog / ILogger 支持),底层仍靠ToString或反射提取字段 - 避免
logger.LogInformation($"User: {user}"),这只会调用ToString(),丢失结构信息 - 如果 record 里有敏感字段(如
PasswordHash),记得在日志前手动剔除或打码,record不提供日志脱敏机制 - 高吞吐场景慎用结构化日志 + 大 record,序列化开销比纯字符串高
record 的设计目标很明确:减少样板代码、强化不可变契约、支持函数式风格的数据操作。它不管 IO、不碰线程安全、不处理敏感数据过滤——这些都得你根据实际场景补全。最容易忽略的是:以为用了 record 就等于“自动可持久化”或“天然可审计”,其实连一行磁盘 I/O 都没发生。











