跳转到主内容
新派编程网 - 新潮编程理念,全新技术课堂!

C#中record类型的用法_C# 9新特性Record详解【最新】

record 不是“更短的 class”,它是编译器强制注入值语义、不可变初始化和浅拷贝行为的语言构造——用错地方会直接导致
InvalidOperationException
、静默共享状态或 EF Core 绑定失败。 record 声明后字段为什么不能改? 因为位置参数生成的属性默认是
init
,不是
set
。这不仅是语法限制,更是编译器在构造函数、
with
表达式和相等性逻辑中统一依赖的契约。
new Person("A", 25) { Name = "B" }
编译报错:对象初始化器不能用于
init
属性(除非显式声明
init
)
var p = new Person("A", 25); p.Name = "B";
同样编译失败 想支持初始化后修改?别硬改 record,换
class
;想部分字段可变但参与比较?把它们放进构造参数列表 with 表达式为什么改了副本,原对象也“变了”?
with
是浅拷贝,它只新建 record 实例,并逐字段复制值——对引用类型字段(如
List
、
Dictionary
),复制的是引用本身,不是内容。 错误写法:
var p1 = new Person("A", new List { "x" }); var p2 = p1 with { Name = "B" }; p2.Tags.Add("y");
→
p1.Tags
也包含
"y"
安全做法:用
IReadOnlyList
+ 构造时新实例化,例如
public record Person(string Name, IReadOnlyList Tags) : this(Name, Tags.ToList().AsReadOnly()) { }
不要指望
with
处理嵌套可变状态——它不递归,也不深克隆 record 能不能直接当 EF Core 实体用? 不能。EF Core 的 change tracker 要求实体具备引用相等性和可写属性,而 record 默认启用值相等、所有属性为
init
,两者根本冲突。 C知道 CSDN推出的一款AI技术问答工具 下载 典型报错:
The property 'Name' has no setter
或
SaveChanges throws InvalidOperationException
正确分工:
class
做数据库实体(带
get; set;
),
record
做 DTO 或查询投影:
context.Products.Select(p => new ProductDto(p.Id, p.Name, p.Price))
若强行用 record 当实体,必须手动加
set
、重写
Equals
、放弃
with
优势——得不偿失 record struct 和 record class 有什么实际区别? 区别不在语法,而在运行时语义:
record class
是引用类型(默认),
record struct
是值类型,影响内存分配、比较方式和泛型约束行为。
public record struct Point(double X, double Y)
:栈上分配,复制开销小,
==
比较是值比较且无需重载
public record class Config(string ApiUrl, int Timeout)
:堆上分配,适合含较大字段或需继承的场景 混用会出问题:比如泛型方法约束
where T : record
不合法,必须写
where T : class
或
where T : struct
;
record struct
无法被继承 最常被忽略的一点:record 的“不可变”只作用于它自己声明的字段,不延伸到字段内部的对象。你控制不了
List
里发生了什么,
with
也不会帮你管——这需要你主动选择只读接口、构造时隔离或手动深拷贝。

相关文章