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