了解 C# 中的record类型——掌握 C# 9 的不可变性功能

3697102e99bf8f7c21f88b863b387300.jpeg

介绍

随着 C# 9 的发布,Microsoft 引入了record,这是一种新的引用类型,可显著简化不可变数据对象的创建。记录特别适用于不可变性和值相等性至关重要的方案,例如数据传输对象 (DTO)、配置对象等。

在这篇文章中,我们将探讨 C# 9 中记录的详细信息、如何有效地使用它们,以及它们可以大大增强代码的一些高级方案。

什么是Records?

C# 9 中的记录提供了一种简洁的方法来定义具有内置值相等性的不可变数据对象。与传统类不同,记录被设计为默认不可变,这意味着它们的状态在创建后无法修改。

record声明

下面是 C# 9 中记录的简单示例:

public record Person(string FirstName, string LastName);

在此示例中,是具有两个属性的记录:和 。编译器会自动为此记录生成多个成员,包括:PersonFirstNameLastName

  • 接受 和 的构造函数。FirstNameLastName

  • 和 的只读属性。FirstNameLastName

  • 位置模式匹配的解构方法。

  • 基于值的相等方法 (, )。EqualsGetHashCode

不可变性和表达式

记录的关键特征之一是不可变性。创建记录后,无法修改其属性。但是,您可以使用 with-expressions 创建更改某些属性的新记录:

var person = new Person("John", "Doe");  
var updatedPerson = person with { LastName = "Smith" };  
  
Console.WriteLine(person); // Output: Person { FirstName = John, LastName = Doe }  
Console.WriteLine(updatedPerson); // Output: Person { FirstName = John, LastName = Smith }

该关键字创建记录的新实例,复制现有值并应用指定的更改。with

值比较

记录提供内置值相等,这意味着具有相同数据的两条记录被视为相等:

var person1 = new Person("John", "Doe");  
var person2 = new Person("John", "Doe");  
  
Console.WriteLine(person1 == person2); // Output: True  
Console.WriteLine(person1.Equals(person2)); // Output: True

此行为与类形成鲜明对比,在类中,默认情况下,相等性基于对象引用。

高级场景

位置record允许您使用位置参数定义记录,使代码更加简洁:

public record Point(int X, int Y);  
  
var point = new Point(1, 2);  
var (x, y) = point; // Deconstruction  
  
Console.WriteLine($"X: {x}, Y: {y}"); // Output: X: 1, Y: 2

自定义record

虽然记录提供了许多内置功能,但您可以通过添加其他方法或覆盖现有方法来自定义它们:

public record Person(string FirstName, string LastName)  
{  
    public string FullName => $"{FirstName} {LastName}";  
  
    public override string ToString() => $"Person: {FullName}";  
}  
  
var person = new Person("John", "Doe");  
Console.WriteLine(person); // Output: Person: John Doe

record和继承

record支持继承,允许您创建不可变类型的层次结构:

public record Person(string FirstName, string LastName);  
public record Employee(string FirstName, string LastName, string Position) : Person(FirstName, LastName);  
  
var employee = new Employee("John", "Doe", "Developer");  
Console.WriteLine(employee); // Output: Employee { FirstName = John, LastName = Doe, Position = Developer }

非位置record

您还可以定义非位置record,从而更好地控制属性初始化:

public record Car  
{  
    public string Make { get; init; }  
    public string Model { get; init; }  
  
    public Car(string make, string model)  
    {  
        Make = make;  
        Model = model;  
    }  
}  
  
var car = new Car("Toyota", "Corolla");  
Console.WriteLine(car); // Output: Car { Make = Toyota, Model = Corolla }

record中的可变性

虽然默认情况下record是不可变的,但如有必要,您可以定义可变record。但是,通常不鼓励这样做,因为它违背了record的主要设计原则:

public record MutablePerson  
{  
    public string FirstName { get; set; }  
    public string LastName { get; set; }  
}

实际应用

record记录在不可变性和值相等很重要的方案中特别有用。以下是一些实际应用:

数据传输对象 (DTO)
record记录是 DTO 的理想选择,DTO 通常需要不可变性和值相等:

public record CustomerDto(int Id, string Name, string Email);

配置对象
record提供了一种方便的方法来定义配置对象,这些对象在初始化后不应更改:

public record AppConfig(string ConnectionString, int MaxRetries);

活动溯源
在事件源系统中,record可用于表示不可变事件:

public record OrderPlaced(Guid OrderId, DateTime PlacedAt, decimal Amount);

结论

C# 9 中的record引入了一种强大而简洁的方法来定义具有内置值相等性的不可变数据对象。通过利用record,您可以创建更可靠和可维护的代码,尤其是在不可变性和值平等至关重要的情况下。

无论您使用的是 DTO、配置对象还是事件源系统,record都提供了一种在 C# 中定义数据结构的现代高效方法。采用此功能可增强您的 C# 编程技能并构建更可靠的应用程序。

如果你喜欢我的文章,请给我一个赞!谢谢

<think> 我们正在讨论C#中的record类型,根据引用[1][2][3][5]的信息,recordC# 9.0引入的一种新的引用类型C# 10.0开始支持record struct值类型),主要用于创建不可变的数据结构,并提供基于值的相等性比较。 引用[4]提到虽然record可以定义可变属性,但这与设计理念相冲突,因此不建议。 回答结构: 1. 介绍record类型的概念和设计目的 2. 主要特性和优势(不可变性、值相等性、简洁语法等) 3. 基本语法和用法(包括位置记录、with表达式等) 4. 使用场景和注意事项 5. 示例代码 注意:引用标识要自然地添加到引用内容的末尾。 </think> ### C# Record类型详解 #### 一、**核心概念与设计目标** Record类型C# 9.0引入的**不可变数据结构**,旨在简化值对象的创建: - **不可变性**:默认所有属性为只读(`init`访问器),创建后状态不可变[^2][^5] - **值语义**:自动实现基于值的相等性比较(非引用地址比较)[^3] - **简洁语法**:减少模板代码(自动生成`ToString()`、`Equals()`等方法)[^1] ```csharp // 基本声明(C# 9+) public record Person(string FirstName, string LastName); ``` #### 二、**核心特性详解** 1. **值相等性** ```csharp var person1 = new Person("Alice", "Smith"); var person2 = new Person("Alice", "Smith"); Console.WriteLine(person1 == person2); // 输出 True(比较属性值) ``` 2. **非破坏性修改(`with`表达式)** ```csharp var updated = person1 with { LastName = "Johnson" }; // 创建新对象,原对象保持不变 ``` 3. **解构支持(Deconstruct)** ```csharp var (firstName, lastName) = person1; // 自动生成解构方法 ``` 4. **继承支持(record可继承record)** ```csharp public record Student(string Name, int Grade) : Person(Name.Split()[0], Name.Split()[1]); ``` #### 三、**两种子类型对比** | 特性 | `record class` (默认) | `record struct` (C# 10+) | |---------------------|------------------------------|---------------------------| | **内存分配** | 堆内存 | 栈内存 | | **相等性比较** | 按属性值比较 | 按属性值比较 | | **可空性** | 支持 | 不支持 | | **适用场景** | 大型对象/跨线程共享 | 小型高频创建对象 | ```csharp // Record struct示例(C# 10+) public readonly record struct Point(int X, int Y); ``` #### 四、**最佳实践与适用场景** 1. **推荐场景**: - 数据传输对象(DTO)[^5] - 配置对象/状态快照 - 领域模型中的值对象(Value Object)[^3] 2. **避免场景**: - 需要频繁修改状态的对象(使用`class`更合适)[^4] - 需要复杂行为逻辑的对象 3. **设计原则**: - 保持最小可变性(避免添加可变属性)[^4] - 优先使用位置记录(positional records) - 配合模式匹配使用增强可读性 ```csharp // 完整示例 public record Book(string ISBN, string Title) { public DateTime ReleaseDate { get; init; } } var book = new Book("978-0-13-468599-7", "Effective C#") { ReleaseDate = new DateTime(2020, 1, 1) }; var updatedBook = book with { ReleaseDate = new DateTime(2023, 6, 1) }; ``` #### 五、**性能与编译原理** 1. **编译器自动生成**: - `Equals()`/`GetHashCode()` - `ToString()`(格式化输出所有属性) - 拷贝构造函数(支持`with`) 2. **内存优化**: - `record struct`避免堆分配 - 不可变性减少线程同步开销[^2] --- ### ⚠️ 注意事项 1. **相等性逻辑**:派生record类型在相等性比较时会检查运行时类型 ```csharp Person p = new Student("Alice", "Smith", 90); Console.WriteLine(p == new Person("Alice", "Smith")); // False(类型不同) ``` 2. **可变性风险**:若包含引用类型属性,需确保其内部状态不可变 ```csharp public record Team(string Name, List<string> Members); // 危险!列表内容可变 ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值