C# record 是一种引用类型,是C# 9.0引入的新特性。它是一种轻量级的、不可变的数据类型,具有只读属性,因此在创建后无法更改,这使得它线程安全。与类不同,record 类型是基于值相等而不是唯一标识符的,这意味着两个 record 实例只要它们的属性相等,就被视为相等。
Records 在数据传输、模式匹配和不可变性方面非常有用。它们提供了更简洁的语法,用于创建不可变的数据对象,避免了手动实现不可变性所需的样板代码。
record
是不可变的,一旦创建,其状态无法修改,确保了线程安全和数据一致性。record
实例的相等性是基于其属性的值,而不是引用。两个record
对象只要它们的属性值相等,就被视为相等。record
提供了一种简化的语法来定义数据类型,避免了手动实现不可变性的繁琐代码。record
在模式匹配中非常有用,可以轻松地与模式匹配语法结合使用,简化了对数据结构的操作。record
,这在某些情况下比使用类更为方便。record
使得代码更易读,尤其是在处理大量数据传输或需要比较相等性的场景中。不可变性的关键在于以下几点:
record
的属性默认是只读的,即它们只能在构造函数中初始化,初始化完成后就不能再修改。这确保了属性值在对象创建后不可变。init
关键字,用于声明属性的初始化器。被init
修饰的属性只能在对象初始化期间被设置,之后将变为只读,实现了不可变性。record
类型的属性将被赋予初始值。一旦初始化完成,属性值不能再被改变。record
类型的不可变性由编译器自动生成的代码保证,确保了对象的状态不会被修改。init关键字:
record
定义数据模型,结合init
关键字,可以确保对象的属性在初始化后不能再被修改。示例:
public record Person
{
public string FirstName { get; init; }
public string LastName { get; init; }
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
}
// 使用record和init创建不可变对象
var person = new Person("John", "Doe");
// 以下操作是不允许的,因为属性是只读的(init关键字的作用)
// person.FirstName = "Jane"; // 编译错误
此外,还可以创建具有可变属性和字段的记录:
public record Person
{
public required string FirstName { get; set; }
public required string LastName { get; set; }
};
记录结构也可以是可变的,包括位置记录结构和没有位置参数的记录结构:
public record struct DataMeasurement(DateTime TakenAt, double Measurement);
public record struct Point
{
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
}
虽然记录可以是可变的,但它们主要用于支持不可变的数据模型。 记录类型提供以下功能:
前面的示例展示了引用类型记录和值类型记录之间的一些区别:
record
或 record class
声明引用类型。 class
关键字是可选项,但可以为读者提高清晰度。 record struct
声明值类型。readonly record struct
中不可变。 它们在 中可变。本文的其余部分将讨论 record class
和 record struct
类型。 每个部分都详细说明了这些差异。 你应该在 record class
和 record struct
之间作出决定,该过程类似于在 class
和 struct
之间作出决定。 record 一词用于描述应用于所有记录类型的行为。 record struct
或 record class
用于描述仅适用于 struct 或 class 类型的行为。 record
类型是在 C# 9 中推出的;record struct
类型是在 C# 10 中推出的。
record
类型的相等性是基于值相等性(value equality)的,意味着当两个record
对象的所有属性值都相等时,它们被认为是相等的。这种相等性是自动生成的,包括Equals
、==
、!=
和GetHashCode
方法,它们默认会比较record
对象的所有属性值。
这意味着,只要两个record
对象的所有属性值相等,它们就被视为相等,即使它们不是同一个对象实例。
例如:
public record Person(string FirstName, string LastName);
var person1 = new Person("John", "Doe");
var person2 = new Person("John", "Doe");
bool isEqual = person1 == person2; // 返回true,因为属性值相等
record
类型的相等性使得比较对象更加直观和简便,因为你只需要关心对象的属性值是否相等,而不必担心对象实例的引用。
模式匹配语法: 使用switch
语句进行模式匹配,根据记录类型的属性值进行不同的操作。例如:
var person = new Person("Alice", 30);
switch (person)
{
case Person p when p.Age > 18:
Console.WriteLine($"{p.Name} is an adult.");
break;
case Person p:
Console.WriteLine($"{p.Name} is a minor.");
break;
}
模式解构: 允许在switch
语句中解构记录类型,直接获取属性的值,使得代码更加简洁:
switch (person)
{
case Person { Name: "Alice", Age: 30 }:
Console.WriteLine("Alice is 30 years old.");
break;
}
嵌套模式匹配: 可以在记录模式中使用其他模式,实现更复杂的匹配逻辑:
switch (person)
{
case Person { Age: > 18, Name: var name }:
Console.WriteLine($"{name} is an adult.");
break;
}
模式匹配中的When条件: 可以使用when
关键字添加额外的条件,使得模式匹配更加灵活:
switch (person)
{
case Person { Age: > 18 } when person.Name.StartsWith("A"):
Console.WriteLine($"{person.Name} is an adult and starts with 'A'.");
break;
}
Record类型是线程安全的。Record类型的线程安全性是通过其不可变性(immutability)来实现的。不可变性意味着一旦对象被创建,其状态就不能被修改。在Record类型中,属性是只读的,一旦对象被创建,这些属性的值就不能被修改,从而确保了对象的不可变性和线程安全性。因此,多个线程可以安全地访问和共享Record对象而无需担心数据被意外修改的问题。
例如,在C#中使用Record类型定义一个不可变的Person对象:
public record Person(string FirstName, string LastName);
在上述例子中,FirstName
和LastName
属性是只读的,它们的值只能在对象初始化的时候被设置,之后不能再被修改,确保了对象的不可变性和线程安全性。
这些缺点并不意味着 Record 类型不可用,而是在选择使用 Record 类型时需要权衡其优势和缺点,以便选择最适合特定场景的数据模型。