结构类型来设计以数据为中心的类型,这些类型提供值相等性,并且很少或没有任何行为。 但对于相对较大的数据模型,结构类型有一些缺点:
- 它们不支持继承。
- 它们在确定值相等性时效率较低。 对于值类型,ValueType.Equals 方法使用反射来查找所有字段。 对于记录,编译器将生成
Equals
方法。 实际上,记录中的值相等性实现的速度明显更快。 - 在某些情况下,它们会占用更多内存,因为每个实例都有所有数据的完整副本。 记录类型是引用类型,因此,记录实例只包含对数据的引用。
记录类型解决了上述的问题
可使用更简洁的语法来声明位置记录。 声明以下位置记录时,编译器会合成更多方法:
- 主构造函数,它的参数与记录声明上的位置参数匹配。
- 主构造函数的每个参数的公共属性。 对于 和
readonly record struct
类型,这些属性为 init-only。 对于record struct
类型,它们是可读写的。 - 用于从记录中提取属性的
Deconstruct
方法。
不可变性
记录类型不一定是不可变的。 可以用 set
访问器和非 readonly
的字段来声明属性。 虽然记录可以是可变的,但它们使创建不可变的数据模型变得更容易。 使用位置语法创建的属性是不可变的。
如果希望以数据为中心的类型是线程安全的,或者哈希表中的哈希代码保持不变,那么不可变性很有用。 它可以防止在通过引用方法传递参数而该方法意外更改参数值时发生的 bug。
值相等性
如果记录类型的两个变量类型相匹配,且所有属性和字段值都一致,那么记录类型的两个变量是相等的。但这两个变量的引用是不同的
Person person1 = new("Nancy", "Davolio", phoneNumbers); Person person2 = new("Nancy", "Davolio", phoneNumbers); Console.WriteLine(person1 == person2); // output: True Console.WriteLine(ReferenceEquals(person1, person2)); // output: False
非破坏性变化
如果需要改变记录实例的不可变属性,可以使用 with
表达式来实现非破坏性变化。 with
表达式创建一个新的记录实例,该实例是现有记录实例的一个副本,修改了指定属性和字段。
public record Person(string FirstName, string LastName) { public string[] PhoneNumbers { get; init; } } public static void Main() { Person person1 = new("Nancy", "Davolio") { PhoneNumbers = new string[1] }; Console.WriteLine(person1); // output: Person { FirstName = Nancy, LastName = Davolio, PhoneNumbers = System.String[] } Person person2 = person1 with { FirstName = "John" }; Console.WriteLine(person2); // output: Person { FirstName = John, LastName = Davolio, PhoneNumbers = System.String[] } Console.WriteLine(person1 == person2); // output: False person2 = person1 with { PhoneNumbers = new string[1] }; Console.WriteLine(person2); // output: Person { FirstName = Nancy, LastName = Davolio, PhoneNumbers = System.String[] } Console.WriteLine(person1 == person2); // output: False person2 = person1 with { }; Console.WriteLine(person1 == person2); // output: True }
继承
一条记录可以从另一条记录继承。 但是,记录不能从类继承,类也不能从记录继承。
C# 10 添加了 record structs,以便你可以将记录定义为值类型。